diff --git a/Cargo.lock b/Cargo.lock index 80aeaf994fda7..e80a6bd46770f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1786,6 +1786,7 @@ dependencies = [ "oxc_codegen", "oxc_diagnostics", "oxc_parser", + "oxc_semantic", "oxc_span", "oxc_syntax", "oxc_traverse", diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index e532ec1ad971b..c5a37dfdc2305 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -27,6 +27,7 @@ oxc_allocator = { workspace = true } oxc_diagnostics = { workspace = true } oxc_syntax = { workspace = true, features = ["to_js_string"] } oxc_traverse = { workspace = true } +oxc_semantic = { workspace = true } dashmap = { workspace = true } indexmap = { workspace = true } diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs index b421bee1621c8..a1fc84f4ea50c 100644 --- a/crates/oxc_transformer/src/context.rs +++ b/crates/oxc_transformer/src/context.rs @@ -7,7 +7,7 @@ use std::{ use oxc_allocator::Allocator; use oxc_ast::{AstBuilder, Trivias}; -use oxc_diagnostics::{Error, OxcDiagnostic}; +use oxc_diagnostics::OxcDiagnostic; use oxc_span::SourceType; use crate::{helpers::module_imports::ModuleImports, TransformOptions}; @@ -65,9 +65,8 @@ impl<'a> TransformCtx<'a> { } } - pub fn take_errors(&self) -> Vec { - let errors: Vec = mem::take(&mut self.errors.borrow_mut()); - errors.into_iter().map(Error::from).collect() + pub fn take_errors(&self) -> Vec { + mem::take(&mut self.errors.borrow_mut()) } /// Add an Error diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index cc3ec9503f218..a4c13473460a2 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -25,10 +25,10 @@ mod helpers { use std::{path::Path, rc::Rc}; -use es2015::ES2015; use oxc_allocator::{Allocator, Vec}; use oxc_ast::{ast::*, AstBuilder, Trivias}; -use oxc_diagnostics::Error; +use oxc_diagnostics::OxcDiagnostic; +use oxc_semantic::{ScopeTree, SemanticBuilder, SymbolTable}; use oxc_span::SourceType; use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; @@ -42,10 +42,17 @@ pub use crate::{ }; use crate::{ context::{Ctx, TransformCtx}, + es2015::ES2015, react::React, typescript::TypeScript, }; +pub struct TransformerReturn { + pub errors: std::vec::Vec, + pub symbols: SymbolTable, + pub scopes: ScopeTree, +} + pub struct Transformer<'a> { ctx: Ctx<'a>, // NOTE: all callbacks must run in order. @@ -79,20 +86,25 @@ impl<'a> Transformer<'a> { } } - /// # Errors - /// - /// Returns `Vec` if any errors were collected during the transformation. - pub fn build(mut self, program: &mut Program<'a>) -> Result<(), std::vec::Vec> { - let TransformCtx { ast: AstBuilder { allocator }, source_text, source_type, .. } = - *self.ctx; - traverse_mut(&mut self, program, source_text, source_type, allocator); - - let errors = self.ctx.take_errors(); - if errors.is_empty() { - Ok(()) - } else { - Err(errors) - } + pub fn build(mut self, program: &mut Program<'a>) -> TransformerReturn { + let (symbols, scopes) = SemanticBuilder::new(self.ctx.source_text, self.ctx.source_type) + .build(program) + .semantic + .into_symbol_table_and_scope_tree(); + let TransformCtx { ast: AstBuilder { allocator }, .. } = *self.ctx; + let (symbols, scopes) = traverse_mut(&mut self, allocator, program, symbols, scopes); + TransformerReturn { errors: self.ctx.take_errors(), symbols, scopes } + } + + pub fn build_with_symbols_and_scopes( + mut self, + symbols: SymbolTable, + scopes: ScopeTree, + program: &mut Program<'a>, + ) -> TransformerReturn { + let TransformCtx { ast: AstBuilder { allocator }, .. } = *self.ctx; + let (symbols, scopes) = traverse_mut(&mut self, allocator, program, symbols, scopes); + TransformerReturn { errors: self.ctx.take_errors(), symbols, scopes } } } diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index fa3b93569f601..0c6282a028dbd 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -30,6 +30,10 @@ pub struct TraverseScoping { // Public methods impl TraverseScoping { + pub fn into_symbol_table_and_scope_tree(self) -> (SymbolTable, ScopeTree) { + (self.symbols, self.scopes) + } + /// Get current scope ID #[inline] pub fn current_scope_id(&self) -> ScopeId { diff --git a/crates/oxc_traverse/src/lib.rs b/crates/oxc_traverse/src/lib.rs index 2209f813e3218..6df0975201243 100644 --- a/crates/oxc_traverse/src/lib.rs +++ b/crates/oxc_traverse/src/lib.rs @@ -62,8 +62,7 @@ use oxc_allocator::Allocator; use oxc_ast::ast::Program; -use oxc_semantic::SemanticBuilder; -use oxc_span::SourceType; +use oxc_semantic::{ScopeTree, SymbolTable}; pub mod ancestor; pub use ancestor::Ancestor; @@ -140,16 +139,14 @@ mod walk; #[allow(unsafe_code)] pub fn traverse_mut<'a, Tr: Traverse<'a>>( traverser: &mut Tr, - program: &mut Program<'a>, - source_text: &'a str, - source_type: SourceType, allocator: &'a Allocator, -) { - let semantic = SemanticBuilder::new(source_text, source_type).build(program).semantic; - let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree(); - + program: &mut Program<'a>, + symbols: SymbolTable, + scopes: ScopeTree, +) -> (SymbolTable, ScopeTree) { let mut ctx = TraverseCtx::new(scopes, symbols, allocator); // SAFETY: Walk functions are constructed to avoid unsoundness unsafe { walk::walk_program(traverser, program as *mut Program, &mut ctx) }; debug_assert!(ctx.ancestors_depth() == 1); + ctx.scoping.into_symbol_table_and_scope_tree() } diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 8aa1e941a2085..1065845d72d1c 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -244,8 +244,9 @@ impl Oxc { options, ) .build(program); - if let Err(errs) = result { - self.save_diagnostics(errs); + if !result.errors.is_empty() { + let errors = result.errors.into_iter().map(Error::from).collect::>(); + self.save_diagnostics(errors); } } diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index 329e90f37ad24..2956bba155c8d 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -149,7 +149,7 @@ pub fn transform( let mut program = parser_ret.program; let transform_options = TransformOptions::from(options); - if let Err(e) = Transformer::new( + let ret = Transformer::new( &allocator, source_path, source_type, @@ -157,9 +157,10 @@ pub fn transform( parser_ret.trivias.clone(), transform_options, ) - .build(&mut program) - { - errors.extend(e.into_iter().map(|error| error.to_string())); + .build(&mut program); + + if !ret.errors.is_empty() { + errors.extend(ret.errors.into_iter().map(|error| error.to_string())); } let mut codegen = CodeGenerator::new(); diff --git a/tasks/benchmark/benches/transformer.rs b/tasks/benchmark/benches/transformer.rs index 931c95d9e4f61..2cabd95cce92d 100644 --- a/tasks/benchmark/benches/transformer.rs +++ b/tasks/benchmark/benches/transformer.rs @@ -31,8 +31,7 @@ fn bench_transformer(criterion: &mut Criterion) { trivias, transform_options, ) - .build(program) - .unwrap(); + .build(program); allocator }); }); diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 05e05c64e9462..9e9539d5f3a00 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -148,11 +148,11 @@ pub trait TestCase { false } - fn transform(&self, path: &Path) -> Result> { + fn transform(&self, path: &Path) -> Result> { let transform_options = match self.transform_options() { Ok(transform_options) => transform_options, Err(json_err) => { - return Err(vec![OxcDiagnostic::error(format!("{json_err:?}")).into()]); + return Err(vec![OxcDiagnostic::error(format!("{json_err:?}"))]); } }; @@ -180,7 +180,11 @@ pub trait TestCase { transform_options.clone(), ) .build(&mut program); - result.map(|()| CodeGenerator::new().build(&program).source_text) + if result.errors.is_empty() { + Ok(CodeGenerator::new().build(&program).source_text) + } else { + Err(result.errors) + } } } @@ -262,15 +266,14 @@ impl TestCase for ConformanceTestCase { ret.trivias.clone(), transform_options.clone(), ); - let result = transformer.build(&mut program); - if result.is_ok() { + let ret = transformer.build(&mut program); + if ret.errors.is_empty() { transformed_code = CodeGenerator::new().build(&program).source_text; } else { - let error = result - .err() - .unwrap() - .iter() - .map(ToString::to_string) + let error = ret + .errors + .into_iter() + .map(|e| Error::from(e).to_string()) .collect::>() .join("\n"); actual_errors = get_babel_error(&error);