diff --git a/Cargo.lock b/Cargo.lock index 3dc1651d4274c..75c9662cfb59b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1877,15 +1877,8 @@ name = "oxc_transform_conformance" version = "0.0.0" dependencies = [ "indexmap", - "oxc_allocator", - "oxc_ast", - "oxc_codegen", - "oxc_diagnostics", - "oxc_parser", - "oxc_semantic", - "oxc_span", + "oxc", "oxc_tasks_common", - "oxc_transformer", "pico-args", "walkdir", ] diff --git a/crates/oxc/src/compiler.rs b/crates/oxc/src/compiler.rs index 2a9f69db18a6d..1b6414b88074a 100644 --- a/crates/oxc/src/compiler.rs +++ b/crates/oxc/src/compiler.rs @@ -58,7 +58,7 @@ pub trait CompilerInterface { } fn compress_options(&self) -> Option { - Some(CompressOptions::all_true()) + None } fn codegen_options(&self) -> Option { @@ -69,6 +69,10 @@ pub trait CompilerInterface { false } + fn check_semantic_error(&self) -> bool { + true + } + fn after_parse(&mut self, _parser_return: &mut ParserReturn) -> ControlFlow<()> { ControlFlow::Continue(()) } @@ -172,7 +176,7 @@ pub trait CompilerInterface { source_path: &Path, ) -> SemanticBuilderReturn<'a> { SemanticBuilder::new(source_text, source_type) - .with_check_syntax_error(true) + .with_check_syntax_error(self.check_semantic_error()) .build_module_record(source_path.to_path_buf(), program) .build(program) } diff --git a/tasks/transform_conformance/Cargo.toml b/tasks/transform_conformance/Cargo.toml index 9bc880c04ca33..5222a1f4aede6 100644 --- a/tasks/transform_conformance/Cargo.toml +++ b/tasks/transform_conformance/Cargo.toml @@ -22,15 +22,8 @@ test = false doctest = false [dependencies] -oxc_ast = { workspace = true } -oxc_span = { workspace = true } -oxc_allocator = { workspace = true } -oxc_parser = { workspace = true } -oxc_codegen = { workspace = true } -oxc_semantic = { workspace = true } -oxc_transformer = { workspace = true } +oxc = { workspace = true, features = ["full"] } oxc_tasks_common = { workspace = true } -oxc_diagnostics = { workspace = true } walkdir = { workspace = true } pico-args = { workspace = true } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 735fb5f5bddd9..0a3dc2b3017fe 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,6 +1,6 @@ commit: 12619ffe -Passed: 466/953 +Passed: 487/953 # All Passed: * babel-plugin-transform-optional-catch-binding @@ -465,37 +465,16 @@ Passed: 466/953 * opts/optimizeConstEnums/input.ts * opts/rewriteImportExtensions/input.ts -# babel-plugin-transform-typescript (109/151) +# babel-plugin-transform-typescript (130/151) * class/accessor-allowDeclareFields-false/input.ts * class/accessor-allowDeclareFields-true/input.ts * enum/mix-references/input.ts * enum/ts5.0-const-foldable/input.ts * exports/declared-types/input.ts -* exports/export-import=/input.ts * exports/interface/input.ts -* imports/elide-type-referenced-in-imports-equal-no/input.ts -* imports/import=-module/input.ts * imports/only-remove-type-imports/input.ts * imports/type-only-export-specifier-2/input.ts * imports/type-only-import-specifier-4/input.ts -* namespace/alias/input.ts -* namespace/clobber-class/input.ts -* namespace/clobber-enum/input.ts -* namespace/clobber-export/input.ts -* namespace/contentious-names/input.ts -* namespace/declare/input.ts -* namespace/declare-global-nested-namespace/input.ts -* namespace/empty-removed/input.ts -* namespace/export/input.ts -* namespace/module-nested/input.ts -* namespace/module-nested-export/input.ts -* namespace/multiple/input.ts -* namespace/mutable-fail/input.ts -* namespace/nested/input.ts -* namespace/nested-namespace/input.ts -* namespace/nested-shorthand/input.ts -* namespace/same-name/input.ts -* namespace/undeclared/input.ts * optimize-const-enums/custom-values/input.ts * optimize-const-enums/custom-values-exported/input.ts * optimize-const-enums/declare/input.ts diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index f7e2a66f305e7..b62b65c0fd1b1 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,16 +1,12 @@ commit: 12619ffe -Passed: 28/35 +Passed: 31/35 # All Passed: * babel-plugin-transform-optional-catch-binding +* babel-plugin-transform-typescript -# babel-plugin-transform-typescript (4/7) -* computed-constant-value/input.ts -* enum-member-reference/input.ts -* export-elimination/input.ts - # babel-plugin-transform-react-jsx (23/27) * refresh/can-handle-implicit-arrow-returns/input.jsx * refresh/registers-identifiers-used-in-jsx-at-definition-site/input.jsx diff --git a/tasks/transform_conformance/src/driver.rs b/tasks/transform_conformance/src/driver.rs new file mode 100644 index 0000000000000..e6ca8fc36cb5d --- /dev/null +++ b/tasks/transform_conformance/src/driver.rs @@ -0,0 +1,94 @@ +use std::{mem, ops::ControlFlow, path::Path}; + +use oxc::{ + ast::ast::Program, + diagnostics::OxcDiagnostic, + semantic::{post_transform_checker::PostTransformChecker, SemanticBuilderReturn}, + span::SourceType, + transformer::{TransformOptions, TransformerReturn}, + CompilerInterface, +}; + +pub struct Driver { + options: TransformOptions, + printed: String, + errors: Vec, + check_semantic: bool, + checker: PostTransformChecker, +} + +impl CompilerInterface for Driver { + fn transform_options(&self) -> Option { + Some(self.options.clone()) + } + + fn check_semantic_error(&self) -> bool { + false + } + + fn handle_errors(&mut self, errors: Vec) { + self.errors.extend(errors); + } + + fn after_codegen(&mut self, printed: String) { + self.printed = printed; + } + + fn after_semantic( + &mut self, + program: &mut Program<'_>, + _semantic_return: &mut SemanticBuilderReturn, + ) -> ControlFlow<()> { + if self.check_semantic { + if let Some(errors) = self.checker.before_transform(program) { + self.errors.extend(errors); + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + } + + fn after_transform( + &mut self, + program: &mut Program<'_>, + transformer_return: &mut TransformerReturn, + ) -> ControlFlow<()> { + if self.check_semantic { + if let Some(errors) = self.checker.after_transform( + &transformer_return.symbols, + &transformer_return.scopes, + program, + ) { + self.errors.extend(errors); + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + } +} + +impl Driver { + pub fn new(options: TransformOptions) -> Self { + Self { + options, + printed: String::new(), + errors: vec![], + check_semantic: false, + checker: PostTransformChecker::default(), + } + } + + pub fn execute( + &mut self, + source_text: &str, + source_type: SourceType, + source_path: &Path, + ) -> Result> { + self.compile(source_text, source_type, source_path); + if self.errors.is_empty() { + Ok(mem::take(&mut self.printed)) + } else { + Err(mem::take(&mut self.errors)) + } + } +} diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index 298ffc158b54e..c3114c8e1720e 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -13,7 +13,7 @@ use oxc_tasks_common::{normalize_path, project_root, Snapshot}; use test_case::TestCaseKind; use walkdir::WalkDir; -mod semantic; +mod driver; mod test_case; #[test] diff --git a/tasks/transform_conformance/src/semantic.rs b/tasks/transform_conformance/src/semantic.rs deleted file mode 100644 index 4f1332202622a..0000000000000 --- a/tasks/transform_conformance/src/semantic.rs +++ /dev/null @@ -1,86 +0,0 @@ -use oxc_ast::{ - ast::{BindingIdentifier, ExportSpecifier, ImportSpecifier, ModuleExportName, Program}, - visit::walk::walk_import_specifier, - Visit, -}; -use oxc_semantic::{ScopeTree, SymbolTable}; - -pub struct SemanticTester { - scopes: ScopeTree, - symbols: SymbolTable, - errors: Vec, -} - -impl SemanticTester { - pub fn new(scopes: ScopeTree, symbols: SymbolTable) -> Self { - Self { scopes, symbols, errors: Vec::new() } - } - - pub fn test(mut self, program: &Program) -> Vec { - self.visit_program(program); - self.errors - } -} - -impl<'a> Visit<'a> for SemanticTester { - fn visit_binding_identifier(&mut self, it: &BindingIdentifier<'a>) { - let symbol_id = it.symbol_id.get(); - if let Some(symbol_id) = symbol_id { - if self.symbols.get_flag(symbol_id).is_empty() { - self.errors.push(format!( - "Expect SymbolFlags for BindingIdentifier({}) to not be empty", - it.name - )); - } - if !self.scopes.has_binding(self.symbols.get_scope_id(symbol_id), &it.name) { - self.errors.push(format!( - "Cannot find BindingIdentifier({}) in the Scope corresponding to the Symbol", - it.name - )); - } - } else { - self.errors.push(format!("Expect BindingIdentifier({}) to have a symbol_id", it.name)); - } - } - fn visit_identifier_reference(&mut self, it: &oxc_ast::ast::IdentifierReference<'a>) { - if let Some(reference_id) = it.reference_id.get() { - let reference = self.symbols.get_reference(reference_id); - if reference.flag().is_empty() { - self.errors.push(format!( - "Expect ReferenceFlags for IdentifierReference({}) to not be empty", - it.name - )); - } - } else { - self.errors - .push(format!("Expect IdentifierReference({}) to have a reference_id", it.name)); - } - } - fn visit_import_specifier(&mut self, it: &ImportSpecifier<'a>) { - let symbol_id = it.local.symbol_id.get(); - if let Some(symbol_id) = symbol_id { - if !self.symbols.get_flag(symbol_id).is_import() { - self.errors.push(format!( - "Expect SymbolFlags for ImportSpecifier({}) should contain SymbolFlags::Import", - it.local.name - )); - } - } - walk_import_specifier(self, it); - } - fn visit_export_specifier(&mut self, it: &ExportSpecifier<'a>) { - if let ModuleExportName::IdentifierReference(ident) = &it.local { - let reference_id = ident.reference_id.get(); - if let Some(symbol_id) = reference_id - .and_then(|reference_id| self.symbols.get_reference(reference_id).symbol_id()) - { - if self.symbols.get_flag(symbol_id).is_empty() { - self.errors.push(format!( - "Expect SymbolFlags for ExportSpecifier({}) should contain SymbolFlags::Import", - it.local - )); - } - } - } - } -} diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 21b1d2de0cfb0..58e881383df14 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -3,19 +3,18 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_allocator::Allocator; -use oxc_codegen::CodeGenerator; -use oxc_diagnostics::{Error, OxcDiagnostic}; -use oxc_parser::Parser; -use oxc_span::{SourceType, VALID_EXTENSIONS}; +use oxc::allocator::Allocator; +use oxc::codegen::CodeGenerator; +use oxc::diagnostics::{Error, OxcDiagnostic}; +use oxc::parser::Parser; +use oxc::span::{SourceType, VALID_EXTENSIONS}; +use oxc::transformer::{BabelOptions, TransformOptions}; use oxc_tasks_common::{normalize_path, print_diff_in_terminal}; -use oxc_transformer::{BabelOptions, TransformOptions, Transformer}; use crate::{ constants::{PLUGINS_NOT_SUPPORTED_YET, SKIP_TESTS}, - fixture_root, packages_root, - semantic::SemanticTester, - TestRunnerEnv, + driver::Driver, + fixture_root, packages_root, TestRunnerEnv, }; #[derive(Debug)] @@ -158,7 +157,6 @@ pub trait TestCase { } }; - let allocator = Allocator::default(); let source_text = fs::read_to_string(path).unwrap(); // Some babel test cases have a js extension, but contain typescript code. @@ -171,22 +169,7 @@ pub trait TestCase { source_type = source_type.with_typescript(true); } - let ret = Parser::new(&allocator, &source_text, source_type).parse(); - let mut program = ret.program; - let result = Transformer::new( - &allocator, - path, - source_type, - &source_text, - ret.trivias.clone(), - transform_options.clone(), - ) - .build(&mut program); - if result.errors.is_empty() { - Ok(CodeGenerator::new().build(&program).source_text) - } else { - Err(result.errors) - } + Driver::new(transform_options.clone()).execute(&source_text, source_type, path) } } @@ -254,44 +237,25 @@ impl TestCase for ConformanceTestCase { let mut transformed_code = String::new(); let mut actual_errors = String::new(); - let mut semantic_errors = Vec::default(); let transform_options = match self.transform_options() { Ok(transform_options) => { - let ret = Parser::new(&allocator, &input, source_type).parse(); - if ret.errors.is_empty() { - let mut program = ret.program; - let transformer = Transformer::new( - &allocator, - &self.path, - source_type, - &input, - ret.trivias.clone(), - transform_options.clone(), - ); - let ret = transformer.build(&mut program); - - semantic_errors = SemanticTester::new(ret.scopes, ret.symbols).test(&program); - - if ret.errors.is_empty() { - transformed_code = CodeGenerator::new().build(&program).source_text; - } else { - let error = ret - .errors + match Driver::new(transform_options.clone()).execute( + &input, + source_type, + &self.path, + ) { + Ok(printed) => { + transformed_code = printed; + } + Err(errors) => { + let error = errors .into_iter() - .map(|e| Error::from(e).to_string()) + .map(|err| err.to_string()) .collect::>() .join("\n"); actual_errors = get_babel_error(&error); } - } else { - let error = ret - .errors - .into_iter() - .map(|err| err.to_string()) - .collect::>() - .join("\n"); - actual_errors = get_babel_error(&error); } Some(transform_options.clone()) } @@ -319,9 +283,8 @@ impl TestCase for ConformanceTestCase { }, ); - let passed = semantic_errors.is_empty() - && (transformed_code == output - || (!output.is_empty() && actual_errors.contains(&output))); + let passed = + transformed_code == output || (!output.is_empty() && actual_errors.contains(&output)); if filtered { println!("Options:"); @@ -350,10 +313,6 @@ impl TestCase for ConformanceTestCase { } } - if !semantic_errors.is_empty() { - println!("\nSemantic Errors:\n\n{}\n", semantic_errors.join("\n")); - } - println!("Passed: {passed}"); } passed