feat!(ast): make Trivias clonable by adding Arc (#3638)

This makes `Trivias` cloneable and stops us from using `Rc::new` and
`Rc::clone` everywhere.

`Trivias` is rarely cloned so an `Arc` should suffice.
This commit is contained in:
Boshen 2024-06-12 13:16:10 +08:00 committed by GitHub
parent 84304b4f8f
commit f6752b482f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 89 additions and 86 deletions

View file

@ -2,7 +2,8 @@
use std::{
collections::btree_map::{BTreeMap, Range},
ops::RangeBounds,
ops::{Deref, RangeBounds},
sync::Arc,
};
use oxc_span::Span;
@ -38,17 +39,28 @@ impl CommentKind {
pub type TriviasMap = BTreeMap<u32, Comment>;
#[derive(Debug, Clone, Default)]
pub struct Trivias(Arc<TriviasImpl>);
#[derive(Debug, Default)]
pub struct Trivias {
pub struct TriviasImpl {
/// Keyed by span.start
comments: TriviasMap,
irregular_whitespaces: Vec<Span>,
}
impl Deref for Trivias {
type Target = TriviasImpl;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl Trivias {
pub fn new(comments: TriviasMap, irregular_whitespaces: Vec<Span>) -> Self {
Self { comments, irregular_whitespaces }
pub fn new(comments: TriviasMap, irregular_whitespaces: Vec<Span>) -> Trivias {
Self(Arc::new(TriviasImpl { comments, irregular_whitespaces }))
}
pub fn comments(&self) -> impl Iterator<Item = (CommentKind, Span)> + '_ {

View file

@ -91,12 +91,12 @@ pub struct Codegen<'a, const MINIFY: bool> {
indentation: u8,
sourcemap_builder: Option<SourcemapBuilder>,
comment_gen_related: Option<CommentGenRelated<'a>>,
comment_gen_related: Option<CommentGenRelated>,
source_code: &'a str,
}
pub struct CommentGenRelated<'a> {
pub trivials: &'a Trivias,
pub struct CommentGenRelated {
pub trivials: Trivias,
/// The key of map is the node start position,
/// the first element of value is the start of the comment
/// the second element of value includes the end of the comment and comment kind.
@ -115,7 +115,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
source_name: &str,
source_code: &'a str,
options: CodegenOptions,
comment_gen_related: Option<CommentGenRelated<'a>>,
comment_gen_related: Option<CommentGenRelated>,
) -> Self {
// Initialize the output code buffer to reduce memory reallocation.
// Minification will reduce by at least half of the original size.

View file

@ -16,7 +16,7 @@ fn test(source_text: &str, expected: &str, options: Option<CodegenOptions>) {
source_text,
options,
Some(oxc_codegen::CommentGenRelated {
trivials: &parse_return.trivias,
trivials: parse_return.trivias,
move_comment_map: FxHashMap::default(),
}),
)

View file

@ -287,7 +287,7 @@ impl IsolatedLintHandler {
let program = allocator.alloc(ret.program);
let semantic_ret = SemanticBuilder::new(javascript_source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.with_check_syntax_error(true)
.build(program);

View file

@ -1,6 +1,6 @@
//! The simplest linter
use std::{env, path::Path, rc::Rc};
use std::{env, path::Path};
use oxc_allocator::Allocator;
use oxc_ast::AstKind;
@ -29,9 +29,8 @@ fn main() -> std::io::Result<()> {
}
let program = allocator.alloc(ret.program);
let semantic_ret = SemanticBuilder::new(&source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.build(program);
let semantic_ret =
SemanticBuilder::new(&source_text, source_type).with_trivias(ret.trivias).build(program);
let mut errors: Vec<OxcDiagnostic> = vec![];

View file

@ -36,7 +36,8 @@ pub struct LintContext<'a> {
impl<'a> LintContext<'a> {
pub fn new(file_path: Box<Path>, semantic: Rc<Semantic<'a>>) -> Self {
let disable_directives =
DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias()).build();
DisableDirectivesBuilder::new(semantic.source_text(), semantic.trivias().clone())
.build();
Self {
semantic,
diagnostics: RefCell::new(vec![]),

View file

@ -46,9 +46,9 @@ impl<'a> DisableDirectives<'a> {
}
}
pub struct DisableDirectivesBuilder<'a, 'b> {
pub struct DisableDirectivesBuilder<'a> {
source_text: &'a str,
trivias: &'b Trivias,
trivias: Trivias,
/// All the disabled rules with their corresponding covering spans
intervals: Lapper<u32, DisabledRule<'a>>,
/// Start of `eslint-disable` or `oxlint-disable`
@ -61,8 +61,8 @@ pub struct DisableDirectivesBuilder<'a, 'b> {
disable_rule_comments: Vec<DisableRuleComment<'a>>,
}
impl<'a, 'b> DisableDirectivesBuilder<'a, 'b> {
pub fn new(source_text: &'a str, trivias: &'b Trivias) -> Self {
impl<'a> DisableDirectivesBuilder<'a> {
pub fn new(source_text: &'a str, trivias: Trivias) -> Self {
Self {
source_text,
trivias,
@ -93,7 +93,7 @@ impl<'a, 'b> DisableDirectivesBuilder<'a, 'b> {
// This algorithm iterates through the comments and builds all intervals
// for matching disable and enable pairs.
// Wrongly ordered matching pairs are not taken into consideration.
for (_, span) in self.trivias.comments() {
for (_, span) in self.trivias.clone().comments() {
let text = span.source_text(self.source_text);
let text = text.trim_start();

View file

@ -264,7 +264,7 @@ impl Runtime {
let program = allocator.alloc(ret.program);
let trivias = Rc::new(ret.trivias);
let trivias = ret.trivias;
// Build the module record to unblock other threads from waiting for too long.
// The semantic model is not built at this stage.

View file

@ -26,7 +26,7 @@ fn main() -> std::io::Result<()> {
let output = Prettier::new(
&allocator,
&source_text,
&ret.trivias,
ret.trivias,
PrettierOptions { semi, trailing_comma: TrailingComma::All, ..PrettierOptions::default() },
)
.build(&ret.program);

View file

@ -70,10 +70,11 @@ impl<'a> DocBuilder<'a> for Prettier<'a> {
}
impl<'a> Prettier<'a> {
#[allow(clippy::needless_pass_by_value)]
pub fn new(
allocator: &'a Allocator,
source_text: &'a str,
trivias: &Trivias,
trivias: Trivias,
options: PrettierOptions,
) -> Self {
Self {

View file

@ -1,4 +1,4 @@
use std::{collections::HashMap, env, path::Path, rc::Rc, sync::Arc};
use std::{collections::HashMap, env, path::Path, sync::Arc};
use itertools::Itertools;
use oxc_allocator::Allocator;
@ -38,7 +38,7 @@ fn main() -> std::io::Result<()> {
let semantic = SemanticBuilder::new(&source_text, source_type)
.with_check_syntax_error(true)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build(program);
if !semantic.errors.is_empty() {

View file

@ -1,4 +1,4 @@
use std::{env, path::Path, rc::Rc, sync::Arc};
use std::{env, path::Path, sync::Arc};
use itertools::Itertools;
use oxc_allocator::Allocator;
@ -23,7 +23,7 @@ fn main() -> std::io::Result<()> {
let semantic = SemanticBuilder::new(&source_text, source_type)
.with_check_syntax_error(true)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build(program);
if !semantic.errors.is_empty() {

View file

@ -1,6 +1,6 @@
//! Semantic Builder
use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc};
use std::{cell::RefCell, path::PathBuf, sync::Arc};
#[allow(clippy::wildcard_imports)]
use oxc_ast::{ast::*, AstKind, Trivias, Visit};
@ -35,7 +35,7 @@ pub struct SemanticBuilder<'a> {
pub source_type: SourceType,
trivias: Rc<Trivias>,
trivias: Trivias,
/// Semantic early errors such as redeclaration errors.
errors: RefCell<Vec<OxcDiagnostic>>,
@ -82,11 +82,11 @@ impl<'a> SemanticBuilder<'a> {
let scope = ScopeTree::default();
let current_scope_id = scope.root_scope_id();
let trivias = Rc::new(Trivias::default());
let trivias = Trivias::default();
Self {
source_text,
source_type,
trivias: Rc::clone(&trivias),
trivias: trivias.clone(),
errors: RefCell::new(vec![]),
current_node_id: AstNodeId::new(0),
current_node_flags: NodeFlags::empty(),
@ -108,9 +108,9 @@ impl<'a> SemanticBuilder<'a> {
}
#[must_use]
pub fn with_trivias(mut self, trivias: Rc<Trivias>) -> Self {
self.trivias = trivias;
self.jsdoc = JSDocBuilder::new(self.source_text, Rc::clone(&self.trivias));
pub fn with_trivias(mut self, trivias: Trivias) -> Self {
self.trivias = trivias.clone();
self.jsdoc = JSDocBuilder::new(self.source_text, trivias);
self
}

View file

@ -1,5 +1,4 @@
use std::collections::BTreeMap;
use std::rc::Rc;
use super::parser::JSDoc;
use crate::jsdoc::JSDocFinder;
@ -9,13 +8,13 @@ use rustc_hash::FxHashSet;
pub struct JSDocBuilder<'a> {
source_text: &'a str,
trivias: Rc<Trivias>,
trivias: Trivias,
attached_docs: BTreeMap<Span, Vec<JSDoc<'a>>>,
leading_comments_seen: FxHashSet<u32>,
}
impl<'a> JSDocBuilder<'a> {
pub fn new(source_text: &'a str, trivias: Rc<Trivias>) -> Self {
pub fn new(source_text: &'a str, trivias: Trivias) -> Self {
Self {
source_text,
trivias,
@ -230,8 +229,6 @@ fn should_attach_jsdoc(kind: &AstKind) -> bool {
#[cfg(test)]
mod test {
use std::rc::Rc;
use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_span::{SourceType, Span};
@ -248,7 +245,7 @@ mod test {
let ret = Parser::new(allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program);
let semantic = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build(program)
.semantic;
semantic

View file

@ -36,8 +36,6 @@ impl<'a> JSDoc<'a> {
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::{Semantic, SemanticBuilder};
use oxc_allocator::Allocator;
use oxc_parser::Parser;
@ -48,7 +46,7 @@ mod test {
let ret = Parser::new(allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program);
let semantic = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build(program)
.semantic;
semantic

View file

@ -182,8 +182,6 @@ impl<'a> JSDocTag<'a> {
#[cfg(test)]
mod test {
use std::rc::Rc;
use crate::{Semantic, SemanticBuilder};
use oxc_allocator::Allocator;
use oxc_parser::Parser;
@ -194,7 +192,7 @@ mod test {
let ret = Parser::new(allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program);
let semantic = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build(program)
.semantic;
semantic
@ -356,7 +354,7 @@ mod test {
("/** @k */", None, ("", " ")),
("/** @k1 {t1} c1 */", Some(("t1", "{t1}")), ("c1", " c1 ")),
(
"/** @k2
"/** @k2
{t2} */",
Some(("t2", "{t2}")),
("", " "),

View file

@ -13,7 +13,7 @@ mod reference;
mod scope;
mod symbol;
use std::{rc::Rc, sync::Arc};
use std::sync::Arc;
pub use petgraph;
pub use petgraph::algo;
@ -57,7 +57,7 @@ pub struct Semantic<'a> {
classes: ClassTable,
trivias: Rc<Trivias>,
trivias: Trivias,
module_record: Arc<ModuleRecord>,

View file

@ -9,7 +9,7 @@ mod module_record_tests {
use oxc_span::{SourceType, Span};
#[allow(clippy::wildcard_imports)]
use oxc_syntax::module_record::*;
use std::{path::PathBuf, rc::Rc, sync::Arc};
use std::{path::PathBuf, sync::Arc};
use crate::SemanticBuilder;
@ -19,7 +19,7 @@ mod module_record_tests {
let ret = Parser::new(&allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program);
let semantic_ret = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build_module_record(PathBuf::new(), program)
.build(program);
Arc::clone(&semantic_ret.semantic.module_record)

View file

@ -1,7 +1,7 @@
mod class_tester;
mod expect;
mod symbol_tester;
use std::{path::PathBuf, rc::Rc, sync::Arc};
use std::{path::PathBuf, sync::Arc};
use itertools::Itertools;
use oxc_allocator::Allocator;
@ -80,7 +80,7 @@ impl<'a> SemanticTester<'a> {
let program = self.allocator.alloc(parse.program);
let semantic_ret = SemanticBuilder::new(self.source_text, self.source_type)
.with_check_syntax_error(true)
.with_trivias(Rc::new(parse.trivias))
.with_trivias(parse.trivias)
.build_module_record(PathBuf::new(), program)
.build(program);

View file

@ -1,4 +1,4 @@
use std::{env, path::Path, rc::Rc};
use std::{env, path::Path};
use oxc_allocator::Allocator;
use oxc_codegen::{Codegen, CodegenOptions};
@ -22,7 +22,6 @@ fn main() {
let source_type = SourceType::from_path(path).unwrap();
let ret = Parser::new(&allocator, &source_text, source_type).parse();
let trivias = Rc::new(ret.trivias);
if !ret.errors.is_empty() {
for error in ret.errors {
@ -47,7 +46,7 @@ fn main() {
},
..Default::default()
};
Transformer::new(&allocator, path, source_type, &source_text, trivias, transform_options)
Transformer::new(&allocator, path, source_type, &source_text, ret.trivias, transform_options)
.build(&mut program)
.unwrap();

View file

@ -17,7 +17,7 @@ pub type Ctx<'a> = Rc<TransformCtx<'a>>;
pub struct TransformCtx<'a> {
errors: RefCell<Vec<OxcDiagnostic>>,
pub trivias: Rc<Trivias>,
pub trivias: Trivias,
pub ast: AstBuilder<'a>,
@ -42,7 +42,7 @@ impl<'a> TransformCtx<'a> {
source_path: &Path,
source_type: SourceType,
source_text: &'a str,
trivias: Rc<Trivias>,
trivias: Trivias,
options: &TransformOptions,
) -> Self {
let filename = source_path

View file

@ -62,7 +62,7 @@ impl<'a> Transformer<'a> {
source_path: &Path,
source_type: SourceType,
source_text: &'a str,
trivias: Rc<Trivias>,
trivias: Trivias,
options: TransformOptions,
) -> Self {
let ctx = Rc::new(TransformCtx::new(

View file

@ -172,7 +172,6 @@ impl Oxc {
.parse();
self.comments = self.map_comments(&ret.trivias);
let trivias = Rc::new(ret.trivias);
self.save_diagnostics(ret.errors.into_iter().map(Error::from).collect::<Vec<_>>());
@ -181,7 +180,7 @@ impl Oxc {
let program = allocator.alloc(ret.program);
let semantic_ret = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::clone(&trivias))
.with_trivias(ret.trivias.clone())
.with_check_syntax_error(true)
.build(program);
@ -208,7 +207,7 @@ impl Oxc {
.preserve_parens(false)
.parse();
let printed =
Prettier::new(&allocator, source_text, &ret.trivias, PrettierOptions::default())
Prettier::new(&allocator, source_text, ret.trivias, PrettierOptions::default())
.build(&ret.program);
self.prettier_formatted_text = printed;
}
@ -218,13 +217,17 @@ impl Oxc {
.allow_return_outside_function(parser_options.allow_return_outside_function)
.preserve_parens(false)
.parse();
let prettier_doc =
Prettier::new(&allocator, source_text, &ret.trivias, PrettierOptions::default())
.doc(&ret.program)
.to_string();
let prettier_doc = Prettier::new(
&allocator,
source_text,
ret.trivias.clone(),
PrettierOptions::default(),
)
.doc(&ret.program)
.to_string();
self.prettier_ir_text = {
let ret = Parser::new(&allocator, &prettier_doc, SourceType::default()).parse();
Prettier::new(&allocator, &prettier_doc, &ret.trivias, PrettierOptions::default())
Prettier::new(&allocator, &prettier_doc, ret.trivias, PrettierOptions::default())
.build(&ret.program)
};
}
@ -232,7 +235,7 @@ impl Oxc {
if run_options.transform() {
let options = TransformOptions::default();
let result =
Transformer::new(&allocator, &path, source_type, source_text, trivias, options)
Transformer::new(&allocator, &path, source_type, source_text, ret.trivias, options)
.build(program);
if let Err(errs) = result {
self.save_diagnostics(errs);

View file

@ -29,7 +29,7 @@ fn bench_linter(criterion: &mut Criterion) {
let ret = Parser::new(&allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program);
let semantic_ret = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::new(ret.trivias))
.with_trivias(ret.trivias)
.build_module_record(PathBuf::new(), program)
.build(program);
let filter = vec![

View file

@ -20,7 +20,7 @@ fn bench_prettier(criterion: &mut Criterion) {
let _ = Prettier::new(
&allocator2,
source_text,
&ret.trivias,
ret.trivias,
PrettierOptions::default(),
)
.build(&ret.program);

View file

@ -1,4 +1,4 @@
use std::{path::Path, rc::Rc};
use std::path::Path;
use oxc_allocator::Allocator;
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
@ -28,7 +28,7 @@ fn bench_transformer(criterion: &mut Criterion) {
Path::new(&file.file_name),
source_type,
source_text,
Rc::new(trivias),
trivias,
transform_options,
)
.build(program)

View file

@ -20,12 +20,12 @@ fn get_result(source_text: &str, source_type: SourceType) -> TestResult {
let allocator = Allocator::default();
let ParserReturn { program, trivias, .. } =
Parser::new(&allocator, source_text, source_type).preserve_parens(false).parse();
let source_text1 = Prettier::new(&allocator, source_text, &trivias, options).build(&program);
let source_text1 = Prettier::new(&allocator, source_text, trivias, options).build(&program);
let allocator = Allocator::default();
let ParserReturn { program, trivias, .. } =
Parser::new(&allocator, &source_text1, source_type).preserve_parens(false).parse();
let source_text2 = Prettier::new(&allocator, &source_text1, &trivias, options).build(&program);
let source_text2 = Prettier::new(&allocator, &source_text1, trivias, options).build(&program);
if source_text1 == source_text2 {
TestResult::Passed

View file

@ -5,7 +5,6 @@ use std::{
panic::UnwindSafe,
path::{Path, PathBuf},
process::{Command, Stdio},
rc::Rc,
};
use console::Style;
@ -333,7 +332,7 @@ pub trait Case: Sized + Sync + Send + UnwindSafe {
let program = allocator.alloc(parser_ret.program);
let semantic_ret = SemanticBuilder::new(source_text, source_type)
.with_trivias(Rc::new(parser_ret.trivias))
.with_trivias(parser_ret.trivias)
.with_check_syntax_error(true)
.build_module_record(PathBuf::new(), program)
.build(program);

View file

@ -1,7 +1,4 @@
use std::{
path::{Path, PathBuf},
rc::Rc,
};
use std::path::{Path, PathBuf};
use oxc_allocator::Allocator;
use oxc_codegen::{Codegen, CodegenOptions};
@ -51,7 +48,7 @@ fn get_result(
source_path,
source_type,
source_text,
Rc::new(parse_result1.trivias),
parse_result1.trivias,
options.clone(),
)
.build(&mut program);
@ -82,7 +79,7 @@ fn get_result(
source_path,
source_type,
&source_text1,
Rc::new(parse_result2.trivias),
parse_result2.trivias,
options,
)
.build(&mut program);

View file

@ -383,7 +383,7 @@ impl TestRunner {
let allocator = Allocator::default();
let source_type = SourceType::from_path(path).unwrap();
let ret = Parser::new(&allocator, source_text, source_type).preserve_parens(false).parse();
Prettier::new(&allocator, source_text, &ret.trivias, prettier_options).build(&ret.program)
Prettier::new(&allocator, source_text, ret.trivias, prettier_options).build(&ret.program)
}
}

View file

@ -1,7 +1,6 @@
use std::{
fs,
path::{Path, PathBuf},
rc::Rc,
};
use oxc_allocator::Allocator;
@ -177,7 +176,7 @@ pub trait TestCase {
path,
source_type,
&source_text,
Rc::new(ret.trivias),
ret.trivias,
transform_options.clone(),
)
.build(&mut program);
@ -271,7 +270,7 @@ impl TestCase for ConformanceTestCase {
&self.path,
source_type,
&input,
Rc::new(ret.trivias),
ret.trivias,
transform_options.clone(),
);
let result = transformer.build(&mut program);