diff --git a/crates/oxc_transformer/src/regexp/mod.rs b/crates/oxc_transformer/src/regexp/mod.rs index 31c3b26a6f343..4dbb65b55ab7c 100644 --- a/crates/oxc_transformer/src/regexp/mod.rs +++ b/crates/oxc_transformer/src/regexp/mod.rs @@ -48,7 +48,7 @@ use oxc_regular_expression::ast::{ CharacterClass, CharacterClassContents, LookAroundAssertionKind, Pattern, Term, }; use oxc_semantic::ReferenceFlags; -use oxc_span::Atom; +use oxc_span::{Atom, SPAN}; use oxc_traverse::{Traverse, TraverseCtx}; use crate::context::Ctx; @@ -113,8 +113,21 @@ impl<'a> Traverse<'a> for RegExp<'a> { expr: &mut Expression<'a>, ctx: &mut oxc_traverse::TraverseCtx<'a>, ) { - let Expression::RegExpLiteral(ref mut regexp) = expr else { - return; + if matches!(expr, Expression::RegExpLiteral(_)) { + self.transform_regexp(expr, ctx); + } + } +} + +impl<'a> RegExp<'a> { + fn transform_regexp( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut oxc_traverse::TraverseCtx<'a>, + ) { + let regexp = match expr { + Expression::RegExpLiteral(regexp) => &mut **regexp, + _ => unreachable!(), }; let flags = regexp.regex.flags; @@ -163,7 +176,7 @@ impl<'a> Traverse<'a> for RegExp<'a> { let callee = { let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "RegExp"); let ident = ctx.create_reference_id( - regexp.span, + SPAN, Atom::from("RegExp"), symbol_id, ReferenceFlags::read(), @@ -173,14 +186,11 @@ impl<'a> Traverse<'a> for RegExp<'a> { let mut arguments = ctx.ast.vec_with_capacity(2); arguments.push( - ctx.ast.argument_expression( - ctx.ast.expression_string_literal(regexp.span, pattern_source), - ), + ctx.ast.argument_expression(ctx.ast.expression_string_literal(SPAN, pattern_source)), ); let flags = regexp.regex.flags.to_string(); - let flags = - ctx.ast.argument_expression(ctx.ast.expression_string_literal(regexp.span, flags)); + let flags = ctx.ast.argument_expression(ctx.ast.expression_string_literal(SPAN, flags)); arguments.push(flags); *expr = ctx.ast.expression_new( @@ -190,34 +200,38 @@ impl<'a> Traverse<'a> for RegExp<'a> { None::, ); } -} -impl<'a> RegExp<'a> { /// Check if the regular expression contains any unsupported syntax. /// /// Based on parsed regular expression pattern. fn has_unsupported_regular_expression_pattern(&self, pattern: &Pattern<'a>) -> bool { - let terms_contains_unsupported = |terms: &[Term]| { - terms.iter().any(|term| match term { - Term::CapturingGroup(_) => self.named_capture_groups, - Term::UnicodePropertyEscape(_) => self.unicode_property_escapes, + pattern.body.body.iter().any(|alternative| { + alternative.body.iter().any(|term| self.term_contains_unsupported(term)) + }) + } + + fn term_contains_unsupported(&self, mut term: &Term) -> bool { + // Loop because `Term::Quantifier` contains a nested `Term` + loop { + match term { + Term::CapturingGroup(_) => return self.named_capture_groups, + Term::UnicodePropertyEscape(_) => return self.unicode_property_escapes, Term::CharacterClass(character_class) => { - self.unicode_property_escapes + return self.unicode_property_escapes && character_class_has_unicode_property_escape(character_class) } Term::LookAroundAssertion(assertion) => { - self.look_behind_assertions + return self.look_behind_assertions && matches!( assertion.kind, LookAroundAssertionKind::Lookbehind | LookAroundAssertionKind::NegativeLookbehind ) } - _ => false, - }) - }; - - pattern.body.body.iter().any(|alternative| terms_contains_unsupported(&alternative.body)) + Term::Quantifier(quantifier) => term = &quantifier.body, + _ => return false, + } + } } }