mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(transformer): pass in symbols and scopes (#3978)
This PR adds a new method `build_with_symbols_and_scopes` to make semantic building optional, there may be prior steps that has the semantic data already built.
This commit is contained in:
parent
b234ddd47c
commit
5845057bff
10 changed files with 64 additions and 46 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1786,6 +1786,7 @@ dependencies = [
|
|||
"oxc_codegen",
|
||||
"oxc_diagnostics",
|
||||
"oxc_parser",
|
||||
"oxc_semantic",
|
||||
"oxc_span",
|
||||
"oxc_syntax",
|
||||
"oxc_traverse",
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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<Error> {
|
||||
let errors: Vec<OxcDiagnostic> = mem::take(&mut self.errors.borrow_mut());
|
||||
errors.into_iter().map(Error::from).collect()
|
||||
pub fn take_errors(&self) -> Vec<OxcDiagnostic> {
|
||||
mem::take(&mut self.errors.borrow_mut())
|
||||
}
|
||||
|
||||
/// Add an Error
|
||||
|
|
|
|||
|
|
@ -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<OxcDiagnostic>,
|
||||
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<Error>` if any errors were collected during the transformation.
|
||||
pub fn build(mut self, program: &mut Program<'a>) -> Result<(), std::vec::Vec<Error>> {
|
||||
let TransformCtx { ast: AstBuilder { allocator }, source_text, source_type, .. } =
|
||||
*self.ctx;
|
||||
traverse_mut(&mut self, program, source_text, source_type, allocator);
|
||||
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 }
|
||||
}
|
||||
|
||||
let errors = self.ctx.take_errors();
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<Vec<_>>();
|
||||
self.save_diagnostics(errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ fn bench_transformer(criterion: &mut Criterion) {
|
|||
trivias,
|
||||
transform_options,
|
||||
)
|
||||
.build(program)
|
||||
.unwrap();
|
||||
.build(program);
|
||||
allocator
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -148,11 +148,11 @@ pub trait TestCase {
|
|||
false
|
||||
}
|
||||
|
||||
fn transform(&self, path: &Path) -> Result<String, Vec<Error>> {
|
||||
fn transform(&self, path: &Path) -> Result<String, Vec<OxcDiagnostic>> {
|
||||
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::<Vec<_>>()
|
||||
.join("\n");
|
||||
actual_errors = get_babel_error(&error);
|
||||
|
|
|
|||
Loading…
Reference in a new issue