diff --git a/Cargo.lock b/Cargo.lock index 3234d13d42520..2f9de16a666e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1409,6 +1409,7 @@ dependencies = [ "oxc_mangler", "oxc_minifier", "oxc_parser", + "oxc_regular_expression", "oxc_semantic", "oxc_sourcemap", "oxc_span", diff --git a/crates/oxc/Cargo.toml b/crates/oxc/Cargo.toml index 9d16fc56d18d2..a67e316ebdfe7 100644 --- a/crates/oxc/Cargo.toml +++ b/crates/oxc/Cargo.toml @@ -28,6 +28,9 @@ required-features = ["full"] [dependencies] oxc_allocator = { workspace = true } oxc_ast = { workspace = true } +oxc_parser = { workspace = true } +oxc_regular_expression = { workspace = true } + oxc_cfg = { workspace = true, optional = true } oxc_codegen = { workspace = true, optional = true } oxc_diagnostics = { workspace = true } @@ -35,7 +38,6 @@ oxc_index = { workspace = true } oxc_isolated_declarations = { workspace = true, optional = true } oxc_mangler = { workspace = true, optional = true } oxc_minifier = { workspace = true, optional = true } -oxc_parser = { workspace = true } oxc_semantic = { workspace = true, optional = true } oxc_sourcemap = { workspace = true, optional = true } oxc_span = { workspace = true } diff --git a/crates/oxc/src/lib.rs b/crates/oxc/src/lib.rs index 7683c39ae84ea..e079632279164 100644 --- a/crates/oxc/src/lib.rs +++ b/crates/oxc/src/lib.rs @@ -33,6 +33,11 @@ pub mod parser { pub use oxc_parser::*; } +pub mod regular_expression { + #[doc(inline)] + pub use oxc_regular_expression::*; +} + pub mod span { #[doc(inline)] pub use oxc_span::*; diff --git a/tasks/coverage/parser_test262.snap b/tasks/coverage/parser_test262.snap index 190fd4dd3005e..a01087b6d2fa6 100644 --- a/tasks/coverage/parser_test262.snap +++ b/tasks/coverage/parser_test262.snap @@ -2,8 +2,11 @@ commit: d62fa93c parser_test262 Summary: AST Parsed : 43765/43765 (100.00%) -Positive Passed: 43765/43765 (100.00%) +Positive Passed: 43764/43765 (100.00%) Negative Passed: 4237/4237 (100.00%) +Expect to Parse: tasks/coverage/test262/test/annexB/language/literals/regexp/legacy-octal-escape.js + + × Regular Expression mismatch: \03 \3 × '0'-prefixed octal literals and octal escape sequences are deprecated ╭─[test262/test/annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js:19:4] diff --git a/tasks/coverage/semantic_test262.snap b/tasks/coverage/semantic_test262.snap index 900514ade67a0..6310120d19f11 100644 --- a/tasks/coverage/semantic_test262.snap +++ b/tasks/coverage/semantic_test262.snap @@ -2,7 +2,7 @@ commit: d62fa93c semantic_test262 Summary: AST Parsed : 43765/43765 (100.00%) -Positive Passed: 43565/43765 (99.54%) +Positive Passed: 43564/43765 (99.54%) tasks/coverage/test262/test/annexB/language/function-code/if-decl-else-decl-a-func-block-scoping.js semantic error: Symbol scope ID mismatch: after transform: SymbolId(3): ScopeId(4294967294) @@ -1119,6 +1119,9 @@ semantic error: Symbol scope ID mismatch: after transform: SymbolId(0): ScopeId(4294967294) rebuilt : SymbolId(0): ScopeId(4294967294) +tasks/coverage/test262/test/annexB/language/literals/regexp/legacy-octal-escape.js +semantic error: Regular Expression mismatch: \03 \3 + tasks/coverage/test262/test/language/module-code/eval-rqstd-once.js semantic error: Bindings mismatch: after transform: ScopeId(0): ["dflt1", "dflt2", "dflt3", "global", "ns1", "ns3"] diff --git a/tasks/coverage/src/driver.rs b/tasks/coverage/src/driver.rs index 0cfaf9d867fb6..acde33772b179 100644 --- a/tasks/coverage/src/driver.rs +++ b/tasks/coverage/src/driver.rs @@ -1,14 +1,19 @@ use std::{collections::HashSet, ops::ControlFlow, path::PathBuf}; use oxc::{ - ast::{ast::Program, Trivias}, + allocator::Allocator, + ast::{ + ast::{Program, RegExpFlags}, + Trivias, + }, codegen::CodegenOptions, diagnostics::OxcDiagnostic, minifier::CompressOptions, parser::{ParseOptions, ParserReturn}, + regular_expression::{ParserOptions, PatternParser}, semantic::{ post_transform_checker::{check_semantic_after_transform, check_semantic_ids}, - SemanticBuilderReturn, + Semantic, SemanticBuilderReturn, }, span::{SourceType, Span}, transformer::{TransformOptions, TransformerReturn}, @@ -78,7 +83,7 @@ impl CompilerInterface for Driver { fn after_semantic( &mut self, program: &mut Program<'_>, - _semantic_return: &mut SemanticBuilderReturn, + ret: &mut SemanticBuilderReturn, ) -> ControlFlow<()> { if self.check_semantic { if let Some(errors) = check_semantic_ids(program) { @@ -86,6 +91,7 @@ impl CompilerInterface for Driver { return ControlFlow::Break(()); } }; + self.check_regular_expressions(&ret.semantic); ControlFlow::Continue(()) } @@ -150,4 +156,41 @@ impl Driver { } false } + + /// Idempotency test for printing regular expressions. + fn check_regular_expressions(&mut self, semantic: &Semantic<'_>) { + let allocator = Allocator::default(); + for literal in semantic.nodes().iter().filter_map(|node| node.kind().as_reg_exp_literal()) { + let Some(pattern) = literal.regex.pattern.as_pattern() else { + continue; + }; + let printed1 = pattern.to_string(); + let flags = literal.regex.flags; + let printed2 = match PatternParser::new( + &allocator, + &printed1, + ParserOptions { + span_offset: 0, + unicode_mode: flags.contains(RegExpFlags::U) || flags.contains(RegExpFlags::V), + unicode_sets_mode: flags.contains(RegExpFlags::V), + }, + ) + .parse() + { + Ok(pattern) => pattern.to_string(), + Err(error) => { + self.errors.push(OxcDiagnostic::error(format!( + "Failed to re-parse `{}`, printed as `/{printed1}/{flags}`, {error}", + literal.span.source_text(semantic.source_text()), + ))); + continue; + } + }; + if printed1 != printed2 { + self.errors.push(OxcDiagnostic::error(format!( + "Regular Expression mismatch: {printed1} {printed2}" + ))); + } + } + } }