diff --git a/Cargo.lock b/Cargo.lock index 9ee5b625294ea..8728caf25f4ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1735,6 +1735,7 @@ dependencies = [ "oxc_allocator", "oxc_ast_macros", "oxc_data_structures", + "oxc_diagnostics", "oxc_estree", "oxc_regular_expression", "oxc_span", diff --git a/crates/oxc_ast/Cargo.toml b/crates/oxc_ast/Cargo.toml index 8a80df9a647c7..36fe87a33eb31 100644 --- a/crates/oxc_ast/Cargo.toml +++ b/crates/oxc_ast/Cargo.toml @@ -22,6 +22,7 @@ doctest = true oxc_allocator = { workspace = true } oxc_ast_macros = { workspace = true } oxc_data_structures = { workspace = true, features = ["inline_string"] } +oxc_diagnostics = { workspace = true } oxc_estree = { workspace = true } oxc_regular_expression = { workspace = true } oxc_span = { workspace = true } diff --git a/crates/oxc_ast/src/ast_impl/literal.rs b/crates/oxc_ast/src/ast_impl/literal.rs index 31bd0e3996bb8..27c5d10645c17 100644 --- a/crates/oxc_ast/src/ast_impl/literal.rs +++ b/crates/oxc_ast/src/ast_impl/literal.rs @@ -130,6 +130,33 @@ impl Display for BigIntLiteral<'_> { } } +impl<'a> RegExpLiteral<'a> { + /// Parse the pattern string. + /// + /// # Errors + /// Returns an error if the pattern is invalid. + pub fn parse_pattern( + &self, + allocator: &'a Allocator, + ) -> oxc_diagnostics::Result> { + let pattern_text = self.regex.pattern.text.as_str(); + #[expect(clippy::cast_possible_truncation)] + let pattern_len = pattern_text.len() as u32; + let literal_span = self.span; + let pattern_span_offset = literal_span.start + 1; // +1 to skip the opening `/` + let flags_span_offset = pattern_span_offset + pattern_len + 1; // +1 to skip the closing `/` + let flags_text = &self.regex.flags.to_inline_string(); + + oxc_regular_expression::LiteralParser::new( + allocator, + pattern_text, + Some(flags_text), + oxc_regular_expression::Options { pattern_span_offset, flags_span_offset }, + ) + .parse() + } +} + impl Display for RegExp<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "/{}/{}", self.pattern.text, self.flags) diff --git a/crates/oxc_regular_expression/src/parser/parser_impl.rs b/crates/oxc_regular_expression/src/parser/parser_impl.rs index 8e4fe7d5bc86b..e77c323f888ef 100644 --- a/crates/oxc_regular_expression/src/parser/parser_impl.rs +++ b/crates/oxc_regular_expression/src/parser/parser_impl.rs @@ -11,18 +11,18 @@ use crate::{ /// Regular expression parser for `/literal/` usage. /// - `pattern_text`: the text between `/` and `/` /// - `flags_text`: the text after the second `/` -pub struct LiteralParser<'a> { +pub struct LiteralParser<'a, 'b> { allocator: &'a Allocator, pattern_text: &'a str, - flags_text: Option<&'a str>, + flags_text: Option<&'b str>, options: Options, } -impl<'a> LiteralParser<'a> { +impl<'a, 'b> LiteralParser<'a, 'b> { pub fn new( allocator: &'a Allocator, pattern_text: &'a str, - flags_text: Option<&'a str>, + flags_text: Option<&'b str>, options: Options, ) -> Self { Self { allocator, pattern_text, flags_text, options } diff --git a/crates/oxc_transformer/src/regexp/mod.rs b/crates/oxc_transformer/src/regexp/mod.rs index b2e903f28f477..3a1e4aedd98a9 100644 --- a/crates/oxc_transformer/src/regexp/mod.rs +++ b/crates/oxc_transformer/src/regexp/mod.rs @@ -45,7 +45,6 @@ //! (actually these would be improvements on ESBuild, not Babel) use oxc_ast::{NONE, ast::*}; -use oxc_diagnostics::Result; use oxc_regular_expression::ast::{ CharacterClass, CharacterClassContents, LookAroundAssertionKind, Pattern, Term, }; @@ -145,21 +144,7 @@ impl<'a> RegExp<'a, '_> { let pattern = if let Some(pattern) = ®exp.regex.pattern.pattern { pattern } else { - #[expect(clippy::cast_possible_truncation)] - let pattern_len = pattern_text.len() as u32; - let literal_span = regexp.span; - let pattern_span_start = literal_span.start + 1; // +1 to skip the opening `/` - let flags_span_start = pattern_span_start + pattern_len + 1; // +1 to skip the closing `/` - let flags_text = - Span::new(flags_span_start, literal_span.end).source_text(self.ctx.source_text); - // Try to parse pattern - match try_parse_pattern( - pattern_text.as_str(), - pattern_span_start, - flags_text, - flags_span_start, - ctx, - ) { + match regexp.parse_pattern(ctx.ast.allocator) { Ok(pattern) => { owned_pattern = Some(pattern); owned_pattern.as_ref().unwrap() @@ -236,16 +221,3 @@ fn character_class_has_unicode_property_escape(character_class: &CharacterClass) _ => false, }) } - -fn try_parse_pattern<'a>( - raw: &'a str, - pattern_span_offset: u32, - flags_text: &'a str, - flags_span_offset: u32, - ctx: &TraverseCtx<'a>, -) -> Result> { - use oxc_regular_expression::{LiteralParser, Options}; - - let options = Options { pattern_span_offset, flags_span_offset }; - LiteralParser::new(ctx.ast.allocator, raw, Some(flags_text), options).parse() -}