Skip to content

Commit f0e48a1

Browse files
committed
improve clean_interned
1 parent f9e1c89 commit f0e48a1

File tree

12 files changed

+73
-87
lines changed

12 files changed

+73
-87
lines changed

crates/oxc_formatter/examples/formatter.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ fn main() -> Result<(), String> {
5454

5555
let formatter = Formatter::new(&allocator, options);
5656
let formatted = formatter.format(&ret.program);
57-
// if show_ir {
58-
// println!("--- IR ---");
59-
// println!("{}", &formatted.document().to_string());
60-
// println!("--- End IR ---\n");
61-
// }
57+
if show_ir {
58+
println!("--- IR ---");
59+
println!("{}", &formatted.document().to_string());
60+
println!("--- End IR ---\n");
61+
}
6262

63-
// println!("--- Formatted Code ---");
63+
println!("--- Formatted Code ---");
6464
let code = formatted.print().map_err(|e| e.to_string())?.into_code();
65-
// println!("{code}");
66-
// println!("--- End Formatted Code ---");
65+
println!("{code}");
66+
println!("--- End Formatted Code ---");
6767
Ok(())
6868
}

crates/oxc_formatter/src/formatter/buffer.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ impl<'buf, 'ast> RemoveSoftLinesBuffer<'buf, 'ast> {
496496
}
497497

498498
/// Removes the soft line breaks from an interned element.
499-
fn clean_interned(&mut self, interned: &Interned<'ast>) -> Interned<'ast> {
499+
fn clean_interned(&mut self, interned: Interned<'ast>) -> Interned<'ast> {
500500
clean_interned(
501501
interned,
502502
&mut self.interned_cache,
@@ -517,12 +517,12 @@ impl<'buf, 'ast> RemoveSoftLinesBuffer<'buf, 'ast> {
517517

518518
// Extracted to function to avoid monomorphization
519519
fn clean_interned<'ast>(
520-
interned: &Interned<'ast>,
520+
interned: Interned<'ast>,
521521
interned_cache: &mut FxHashMap<Interned<'ast>, Interned<'ast>>,
522522
condition_content_stack: &mut Vec<Condition>,
523523
allocator: &'ast Allocator,
524524
) -> Interned<'ast> {
525-
if let Some(cleaned) = interned_cache.get(interned) {
525+
if let Some(cleaned) = interned_cache.get(&interned) {
526526
cleaned.clone()
527527
} else {
528528
// Find the first soft line break element, interned element, or conditional expanded
@@ -531,21 +531,22 @@ fn clean_interned<'ast>(
531531
FormatElement::Line(LineMode::Soft | LineMode::SoftOrSpace)
532532
| FormatElement::Tag(Tag::StartConditionalContent(_) | Tag::EndConditionalContent)
533533
| FormatElement::BestFitting(_) => {
534-
// TODO: avoid using clone here
535-
536534
let mut cleaned =
537535
ArenaVec::from_iter_in(interned[..index].iter().cloned(), allocator);
538536
Some((cleaned, &interned[index..]))
539537
}
540538
FormatElement::Interned(inner) => {
541-
let cleaned_inner =
542-
clean_interned(inner, interned_cache, condition_content_stack, allocator);
539+
let cleaned_inner = clean_interned(
540+
inner.clone(),
541+
interned_cache,
542+
condition_content_stack,
543+
allocator,
544+
);
543545

544546
if &cleaned_inner == inner {
545547
None
546548
} else {
547549
let mut cleaned = ArenaVec::with_capacity_in(interned.len(), allocator);
548-
// TODO: avoid using clone here
549550
cleaned.extend(interned[..index].iter().cloned());
550551
cleaned.push(FormatElement::Interned(cleaned_inner));
551552
Some((cleaned, &interned[index + 1..]))
@@ -580,7 +581,7 @@ fn clean_interned<'ast>(
580581

581582
FormatElement::Interned(interned) => {
582583
cleaned.push(FormatElement::Interned(clean_interned(
583-
interned,
584+
interned.clone(),
584585
interned_cache,
585586
condition_content_stack,
586587
allocator,
@@ -596,21 +597,20 @@ fn clean_interned<'ast>(
596597
}
597598
}
598599

599-
Interned::new(ArenaVec::from_iter_in(cleaned, allocator))
600+
Interned::new(cleaned)
600601
}
601602
// No change necessary, return existing interned element
602603
None => interned.clone(),
603604
};
604605

605-
interned_cache.insert(interned.clone(), result.clone());
606+
interned_cache.insert(interned, result.clone());
606607
result
607608
}
608609
}
609610

610611
impl<'ast> Buffer<'ast> for RemoveSoftLinesBuffer<'_, 'ast> {
611612
fn write_element(&mut self, element: FormatElement<'ast>) -> FormatResult<()> {
612-
let mut element_stack = Vec::new();
613-
element_stack.push(element);
613+
let mut element_stack = Vec::from_iter([element]);
614614
while let Some(element) = element_stack.pop() {
615615
match element {
616616
FormatElement::Tag(Tag::StartConditionalContent(condition)) => {
@@ -628,14 +628,14 @@ impl<'ast> Buffer<'ast> for RemoveSoftLinesBuffer<'_, 'ast> {
628628
self.inner.write_element(FormatElement::Space)?;
629629
}
630630
FormatElement::Interned(interned) => {
631-
let cleaned = self.clean_interned(&interned);
631+
let cleaned = self.clean_interned(interned);
632632
self.inner.write_element(FormatElement::Interned(cleaned))?;
633633
}
634634
// Since this buffer aims to simulate infinite print width, we don't need to retain the best fitting.
635635
// Just extract the flattest variant and then handle elements within it.
636636
FormatElement::BestFitting(best_fitting) => {
637637
let most_flat = best_fitting.most_flat();
638-
most_flat.iter().rev().for_each(|element| element_stack.push(element.clone()));
638+
element_stack.extend(most_flat.iter().rev().cloned());
639639
}
640640
element => self.inner.write_element(element)?,
641641
}

crates/oxc_formatter/src/formatter/builders.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2550,7 +2550,7 @@ impl<'ast> Format<'ast> for BestFitting<'_, 'ast> {
25502550
buffer.write_fmt(Arguments::from(variant))?;
25512551
buffer.write_element(FormatElement::Tag(EndEntry))?;
25522552

2553-
formatted_variants.push(buffer.take_vec().into_boxed_slice());
2553+
formatted_variants.push(buffer.take_vec().into_bump_slice());
25542554
}
25552555

25562556
let formatted_variants =

crates/oxc_formatter/src/formatter/format_element/document.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use crate::{
1919
use crate::{format, write};
2020

2121
/// A formatted document.
22-
#[derive(Debug)]
22+
#[derive(Debug, Clone, Eq, PartialEq, Default)]
2323
pub struct Document<'a> {
24-
elements: ArenaVec<'a, FormatElement<'a>>,
24+
elements: &'a [FormatElement<'a>],
2525
}
2626

2727
impl Document<'_> {
@@ -142,24 +142,24 @@ impl Document<'_> {
142142

143143
impl<'a> From<ArenaVec<'a, FormatElement<'a>>> for Document<'a> {
144144
fn from(elements: ArenaVec<'a, FormatElement<'a>>) -> Self {
145-
Self { elements }
145+
Self { elements: elements.into_bump_slice() }
146146
}
147147
}
148148

149149
impl<'a> Deref for Document<'a> {
150150
type Target = [FormatElement<'a>];
151151

152152
fn deref(&self) -> &Self::Target {
153-
self.elements.as_slice()
153+
self.elements
154154
}
155155
}
156156

157157
impl std::fmt::Display for Document<'_> {
158158
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159159
let allocator = Allocator::default();
160160
let context = FormatContext::dummy(&allocator);
161-
let formatted = format!(context, [self.elements.as_slice()])
162-
.expect("Formatting not to throw any FormatErrors");
161+
let formatted =
162+
format!(context, [self.elements]).expect("Formatting not to throw any FormatErrors");
163163

164164
f.write_str(formatted.print().expect("Expected a valid document").as_code())
165165
}

crates/oxc_formatter/src/formatter/format_element/mod.rs

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
pub mod document;
22
pub mod tag;
33

4-
use std::cell::Cell;
54
// #[cfg(target_pointer_width = "64")]
65
// use biome_rowan::static_assert;
76
use std::hash::{Hash, Hasher};
8-
use std::mem::{self, ManuallyDrop};
97
use std::num::NonZeroU32;
8+
use std::ptr;
109
use std::{borrow::Cow, ops::Deref, rc::Rc};
1110

1211
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
@@ -53,7 +52,7 @@ const _: () = {
5352
/// Language agnostic IR for formatting source code.
5453
///
5554
/// Use the helper functions like [crate::builders::space], [crate::builders::soft_line_break] etc. defined in this file to create elements.
56-
#[derive(Clone)]
55+
#[derive(Clone, Eq, PartialEq)]
5756
pub enum FormatElement<'a> {
5857
/// A space token, see [crate::builders::space] for documentation.
5958
Space,
@@ -150,29 +149,25 @@ impl PrintMode {
150149
}
151150

152151
#[derive(Clone)]
153-
pub struct Interned<'a>(ManuallyDrop<Rc<ArenaVec<'a, FormatElement<'a>>>>);
152+
pub struct Interned<'a>(&'a [FormatElement<'a>]);
154153

155154
impl<'a> Interned<'a> {
156155
pub(super) fn new(content: ArenaVec<'a, FormatElement<'a>>) -> Self {
157-
Self(ManuallyDrop::new(Rc::new(content)))
156+
Self(content.into_bump_slice())
158157
}
159158
}
160159

161160
impl PartialEq for Interned<'_> {
162161
fn eq(&self, other: &Interned<'_>) -> bool {
163-
std::ptr::eq(&raw const self.0, &raw const other.0)
162+
ptr::eq(self.0, other.0)
164163
}
165164
}
166165

167166
impl Eq for Interned<'_> {}
168167

169168
impl Hash for Interned<'_> {
170-
fn hash<H>(&self, hasher: &mut H)
171-
where
172-
H: Hasher,
173-
{
174-
Address::from_ptr(&raw const self.0).hash(hasher);
175-
// Rc::as_ptr(&self.0).hash(hasher);
169+
fn hash<H: Hasher>(&self, state: &mut H) {
170+
self.0.as_ptr().addr().hash(state);
176171
}
177172
}
178173

@@ -186,7 +181,7 @@ impl<'a> Deref for Interned<'a> {
186181
type Target = [FormatElement<'a>];
187182

188183
fn deref(&self) -> &Self::Target {
189-
&self.0
184+
self.0
190185
}
191186
}
192187

@@ -305,19 +300,12 @@ impl FormatElements for FormatElement<'_> {
305300
/// can pick the best fitting variant.
306301
///
307302
/// Best fitting is defined as the variant that takes the most horizontal space but fits on the line.
303+
#[derive(Clone, Eq, PartialEq)]
308304
pub struct BestFittingElement<'a> {
309305
/// The different variants for this element.
310306
/// The first element is the one that takes up the most space horizontally (the most flat),
311307
/// The last element takes up the least space horizontally (but most horizontal space).
312-
variants: ArenaBox<'a, [ArenaBox<'a, [FormatElement<'a>]>]>,
313-
}
314-
315-
impl Clone for BestFittingElement<'_> {
316-
fn clone(&self) -> Self {
317-
// SAFETY: NOT sure if it is safe, but needed to implement Clone.
318-
// TODO: Once everything works, this should be replaced with a proper clone implementation.
319-
Self { variants: unsafe { mem::transmute_copy(self) } }
320-
}
308+
variants: &'a [&'a [FormatElement<'a>]],
321309
}
322310

323311
impl<'a> BestFittingElement<'a> {
@@ -330,15 +318,13 @@ impl<'a> BestFittingElement<'a> {
330318
/// ## Safety
331319
/// The slice must contain at least two variants.
332320
#[doc(hidden)]
333-
pub unsafe fn from_vec_unchecked(
334-
variants: ArenaVec<'a, ArenaBox<'a, [FormatElement<'a>]>>,
335-
) -> Self {
321+
pub unsafe fn from_vec_unchecked(variants: ArenaVec<'a, &'a [FormatElement<'a>]>) -> Self {
336322
debug_assert!(
337323
variants.len() >= 2,
338324
"Requires at least the least expanded and most expanded variants"
339325
);
340326

341-
Self { variants: variants.into_boxed_slice() }
327+
Self { variants: variants.into_bump_slice() }
342328
}
343329

344330
/// Returns the most expanded variant
@@ -348,8 +334,8 @@ impl<'a> BestFittingElement<'a> {
348334
)
349335
}
350336

351-
pub fn variants(&self) -> &[ArenaBox<'a, [FormatElement<'a>]>] {
352-
&self.variants
337+
pub fn variants(&self) -> &[&'a [FormatElement<'a>]] {
338+
self.variants
353339
}
354340

355341
/// Returns the least expanded variant
@@ -362,7 +348,7 @@ impl<'a> BestFittingElement<'a> {
362348

363349
impl std::fmt::Debug for BestFittingElement<'_> {
364350
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365-
f.debug_list().entries(&*self.variants).finish()
351+
f.debug_list().entries(self.variants).finish()
366352
}
367353
}
368354

crates/oxc_formatter/src/formatter/formatter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl<'buf, 'ast> Formatter<'buf, 'ast> {
2727
Self { buffer }
2828
}
2929

30-
pub fn allocator(&self) -> &Allocator {
30+
pub fn allocator(&self) -> &'ast Allocator {
3131
self.context().allocator()
3232
}
3333

crates/oxc_formatter/src/formatter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub use self::{
7373
};
7474
use self::{format_element::document::Document, group_id::UniqueGroupIdBuilder, prelude::TagKind};
7575

76-
#[derive(Debug)]
76+
#[derive(Debug, Clone)]
7777
pub struct Formatted<'a> {
7878
document: Document<'a>,
7979
context: FormatContext<'a>,

crates/oxc_formatter/src/formatter/printer/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,7 @@ impl<'a> Printer<'a> {
357357

358358
// Remove the content slice because printing needs the variant WITH the start entry
359359
let popped_slice = queue.pop_slice();
360-
// TODO: We should revisit here when everything works.
361-
// debug_assert_eq!(popped_slice, Some(content));
360+
debug_assert_eq!(popped_slice, Some(content));
362361

363362
if variant_fits {
364363
queue.extend_back(variant);
@@ -512,7 +511,7 @@ impl<'a> Printer<'a> {
512511
}
513512
}
514513

515-
if queue.top().is_some_and(|e| matches!(e, FormatElement::Tag(Tag::EndFill))) {
514+
if queue.top() == Some(&FormatElement::Tag(EndFill)) {
516515
Ok(())
517516
} else {
518517
invalid_end_tag(TagKind::Fill, stack.top_kind())

crates/oxc_formatter/src/ir_transform/sort_imports/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ mod import_unit;
22
mod partitioned_chunk;
33
mod source_line;
44

5-
use oxc_allocator::{Allocator, Vec as ArenaVec};
65
use std::mem;
76

7+
use oxc_allocator::{Allocator, Vec as ArenaVec};
8+
89
use crate::{
910
formatter::format_element::{FormatElement, LineMode, document::Document},
1011
options,
@@ -32,9 +33,7 @@ impl SortImportsTransform {
3233
pub fn transform<'a>(&self, document: &Document<'a>, allocator: &'a Allocator) -> Document<'a> {
3334
// Early return for empty files
3435
if document.len() == 1 && matches!(document[0], FormatElement::Line(LineMode::Hard)) {
35-
// SAFETY: Not sure how to solve it.
36-
// TODO: Refactor `Document` to avoid this.
37-
return unsafe { mem::transmute_copy(document) };
36+
return document.clone();
3837
}
3938

4039
let prev_elements: &[FormatElement<'a>] = document;

0 commit comments

Comments
 (0)