From 59abf27d9563165883491e7ccaa89f0d78433559 Mon Sep 17 00:00:00 2001 From: rzvxa <3788964+rzvxa@users.noreply.github.com> Date: Tue, 3 Sep 2024 02:36:37 +0000 Subject: [PATCH] feat(ast, parser): add `oxc_regular_expression` types to the parser and AST. (#5256) closes #5060 --- .github/.generated_ast_watch_list.yml | 2 + Cargo.lock | 5 + crates/oxc_ast/Cargo.toml | 10 +- crates/oxc_ast/src/ast/literal.rs | 25 +- crates/oxc_ast/src/ast_impl/literal.rs | 29 ++ .../oxc_ast/src/generated/assert_layouts.rs | 321 ++++++++++++++++- .../oxc_ast/src/generated/derive_clone_in.rs | 11 + crates/oxc_codegen/src/gen.rs | 5 +- .../src/rules/eslint/no_control_regex.rs | 34 +- .../src/rules/eslint/no_div_regex.rs | 2 +- .../rules/eslint/no_empty_character_class.rs | 4 +- .../src/rules/eslint/no_regex_spaces.rs | 11 +- .../src/rules/unicorn/no_hex_escape.rs | 4 +- .../unicorn/prefer_string_replace_all.rs | 12 +- .../unicorn/prefer_string_starts_ends_with.rs | 33 +- crates/oxc_linter/src/utils/unicorn.rs | 3 +- .../oxc_parser/examples/regular_expression.rs | 25 +- crates/oxc_parser/src/js/expression.rs | 25 +- crates/oxc_prettier/src/format/mod.rs | 2 +- crates/oxc_regular_expression/Cargo.toml | 15 + crates/oxc_regular_expression/src/ast.rs | 253 +++++++++---- .../src/generated/derive_clone_in.rs | 338 ++++++++++++++++++ crates/oxc_regular_expression/src/lib.rs | 4 + .../src/generators/assert_layouts.rs | 4 + tasks/ast_tools/src/main.rs | 1 + 25 files changed, 1013 insertions(+), 165 deletions(-) create mode 100644 crates/oxc_regular_expression/src/generated/derive_clone_in.rs diff --git a/.github/.generated_ast_watch_list.yml b/.github/.generated_ast_watch_list.yml index df3fdfb8383ed..b31c1ee6ead2e 100644 --- a/.github/.generated_ast_watch_list.yml +++ b/.github/.generated_ast_watch_list.yml @@ -10,12 +10,14 @@ src: - 'crates/oxc_syntax/src/operator.rs' - 'crates/oxc_span/src/span/types.rs' - 'crates/oxc_span/src/source_type/types.rs' + - 'crates/oxc_regular_expression/src/ast.rs' - 'crates/oxc_ast/src/generated/assert_layouts.rs' - 'crates/oxc_ast/src/generated/ast_kind.rs' - 'crates/oxc_ast/src/generated/ast_builder.rs' - 'crates/oxc_ast/src/generated/visit.rs' - 'crates/oxc_ast/src/generated/visit_mut.rs' - 'crates/oxc_ast/src/generated/derive_clone_in.rs' + - 'crates/oxc_regular_expression/src/generated/derive_clone_in.rs' - 'crates/oxc_syntax/src/generated/derive_clone_in.rs' - 'crates/oxc_ast/src/generated/derive_get_span.rs' - 'crates/oxc_ast/src/generated/derive_get_span_mut.rs' diff --git a/Cargo.lock b/Cargo.lock index d419fc2dbdc83..fced24a1e7f0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1444,6 +1444,7 @@ dependencies = [ "num-bigint", "oxc_allocator", "oxc_ast_macros", + "oxc_regular_expression", "oxc_span", "oxc_syntax", "serde", @@ -1799,11 +1800,15 @@ name = "oxc_regular_expression" version = "0.26.0" dependencies = [ "oxc_allocator", + "oxc_ast_macros", "oxc_diagnostics", "oxc_span", "phf 0.11.2", "rustc-hash", + "serde", + "tsify", "unicode-id-start", + "wasm-bindgen", ] [[package]] diff --git a/crates/oxc_ast/Cargo.toml b/crates/oxc_ast/Cargo.toml index 915e34ec4850c..2e62e2789e372 100644 --- a/crates/oxc_ast/Cargo.toml +++ b/crates/oxc_ast/Cargo.toml @@ -19,10 +19,11 @@ workspace = true doctest = false [dependencies] -oxc_allocator = { workspace = true } -oxc_ast_macros = { workspace = true } -oxc_span = { workspace = true } -oxc_syntax = { workspace = true } +oxc_allocator = { workspace = true } +oxc_ast_macros = { workspace = true } +oxc_span = { workspace = true } +oxc_syntax = { workspace = true } +oxc_regular_expression = { workspace = true } bitflags = { workspace = true } num-bigint = { workspace = true } @@ -44,4 +45,5 @@ serialize = [ "oxc_span/serialize", "oxc_syntax/serialize", "oxc_syntax/to_js_string", + "oxc_regular_expression/serialize" ] diff --git a/crates/oxc_ast/src/ast/literal.rs b/crates/oxc_ast/src/ast/literal.rs index 333f2111af118..076ee59a275eb 100644 --- a/crates/oxc_ast/src/ast/literal.rs +++ b/crates/oxc_ast/src/ast/literal.rs @@ -10,8 +10,9 @@ use std::hash::Hash; use bitflags::bitflags; -use oxc_allocator::CloneIn; +use oxc_allocator::{Box, CloneIn}; use oxc_ast_macros::ast; +use oxc_regular_expression::ast::Pattern; use oxc_span::{Atom, GetSpan, GetSpanMut, Span}; use oxc_syntax::number::{BigintBase, NumberBase}; #[cfg(feature = "serialize")] @@ -86,7 +87,7 @@ pub struct BigIntLiteral<'a> { /// /// #[ast(visit)] -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Hash)] #[generate_derive(CloneIn, GetSpan, GetSpanMut)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[serde(tag = "type")] @@ -103,16 +104,32 @@ pub struct RegExpLiteral<'a> { /// /// #[ast] -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Hash)] #[generate_derive(CloneIn)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct RegExp<'a> { /// The regex pattern between the slashes - pub pattern: Atom<'a>, + pub pattern: RegExpPattern<'a>, /// Regex flags after the closing slash pub flags: RegExpFlags, } +/// A regular expression pattern +/// +/// This pattern may or may not be parsed. +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] +pub enum RegExpPattern<'a> { + /// Unparsed pattern, Contains a string slice of the pattern. + Raw(&'a str) = 0, + /// An invalid pattern, Contains a string slice of the pattern. + Invalid(/* raw */ &'a str) = 1, + /// A parsed pattern, Read [Pattern] for more details. + Pattern(Box<'a, Pattern<'a>>) = 2, +} + #[ast] #[derive(Debug, Clone, Hash)] #[generate_derive(CloneIn)] diff --git a/crates/oxc_ast/src/ast_impl/literal.rs b/crates/oxc_ast/src/ast_impl/literal.rs index c95788307b4c8..4eee856867634 100644 --- a/crates/oxc_ast/src/ast_impl/literal.rs +++ b/crates/oxc_ast/src/ast_impl/literal.rs @@ -119,6 +119,35 @@ impl<'a> fmt::Display for RegExp<'a> { } } +impl<'a> RegExpPattern<'a> { + pub fn len(&self) -> usize { + match self { + Self::Raw(it) | Self::Invalid(it) => it.len(), + Self::Pattern(it) => it.span.size() as usize, + } + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn source_text(&self, source_text: &'a str) -> &'a str { + match self { + Self::Raw(raw) | Self::Invalid(raw) => raw, + Self::Pattern(pat) => pat.span.source_text(source_text), + } + } +} + +impl<'a> fmt::Display for RegExpPattern<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Raw(it) | Self::Invalid(it) => write!(f, "{it}"), + Self::Pattern(it) => it.fmt(f), + } + } +} + impl TryFrom for RegExpFlags { type Error = char; diff --git a/crates/oxc_ast/src/generated/assert_layouts.rs b/crates/oxc_ast/src/generated/assert_layouts.rs index 9d4160d4f971c..d25ce8f6b37cb 100644 --- a/crates/oxc_ast/src/generated/assert_layouts.rs +++ b/crates/oxc_ast/src/generated/assert_layouts.rs @@ -6,6 +6,9 @@ use std::mem::{align_of, offset_of, size_of}; #[allow(clippy::wildcard_imports)] use crate::ast::*; +#[allow(clippy::wildcard_imports)] +use oxc_regular_expression::ast::*; + #[cfg(target_pointer_width = "64")] const _: () = { assert!(size_of::() == 12usize); @@ -30,16 +33,19 @@ const _: () = { assert!(offset_of!(BigIntLiteral, raw) == 8usize); assert!(offset_of!(BigIntLiteral, base) == 24usize); - assert!(size_of::() == 32usize); + assert!(size_of::() == 40usize); assert!(align_of::() == 8usize); assert!(offset_of!(RegExpLiteral, span) == 0usize); assert!(offset_of!(RegExpLiteral, value) == 8usize); assert!(offset_of!(RegExpLiteral, regex) == 8usize); - assert!(size_of::() == 24usize); + assert!(size_of::() == 32usize); assert!(align_of::() == 8usize); assert!(offset_of!(RegExp, pattern) == 0usize); - assert!(offset_of!(RegExp, flags) == 16usize); + assert!(offset_of!(RegExp, flags) == 24usize); + + assert!(size_of::() == 24usize); + assert!(align_of::() == 8usize); assert!(size_of::() == 0usize); assert!(align_of::() == 1usize); @@ -1405,6 +1411,156 @@ const _: () = { assert!(size_of::() == 1usize); assert!(align_of::() == 1usize); + + assert!(size_of::() == 72usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(RegularExpression, span) == 0usize); + assert!(offset_of!(RegularExpression, pattern) == 8usize); + assert!(offset_of!(RegularExpression, flags) == 56usize); + + assert!(size_of::() == 16usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Flags, span) == 0usize); + assert!(offset_of!(Flags, global) == 8usize); + assert!(offset_of!(Flags, ignore_case) == 9usize); + assert!(offset_of!(Flags, multiline) == 10usize); + assert!(offset_of!(Flags, unicode) == 11usize); + assert!(offset_of!(Flags, sticky) == 12usize); + assert!(offset_of!(Flags, dot_all) == 13usize); + assert!(offset_of!(Flags, has_indices) == 14usize); + assert!(offset_of!(Flags, unicode_sets) == 15usize); + + assert!(size_of::() == 48usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(Pattern, span) == 0usize); + assert!(offset_of!(Pattern, body) == 8usize); + + assert!(size_of::() == 40usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(Disjunction, span) == 0usize); + assert!(offset_of!(Disjunction, body) == 8usize); + + assert!(size_of::() == 40usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(Alternative, span) == 0usize); + assert!(offset_of!(Alternative, body) == 8usize); + + assert!(size_of::() == 24usize); + assert!(align_of::() == 8usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(BoundaryAssertion, span) == 0usize); + assert!(offset_of!(BoundaryAssertion, kind) == 8usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 56usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(LookAroundAssertion, span) == 0usize); + assert!(offset_of!(LookAroundAssertion, kind) == 8usize); + assert!(offset_of!(LookAroundAssertion, body) == 16usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 64usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(Quantifier, span) == 0usize); + assert!(offset_of!(Quantifier, min) == 8usize); + assert!(offset_of!(Quantifier, max) == 16usize); + assert!(offset_of!(Quantifier, greedy) == 32usize); + assert!(offset_of!(Quantifier, body) == 40usize); + + assert!(size_of::() == 16usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Character, span) == 0usize); + assert!(offset_of!(Character, kind) == 8usize); + assert!(offset_of!(Character, value) == 12usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(CharacterClassEscape, span) == 0usize); + assert!(offset_of!(CharacterClassEscape, kind) == 8usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 48usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(UnicodePropertyEscape, span) == 0usize); + assert!(offset_of!(UnicodePropertyEscape, negative) == 8usize); + assert!(offset_of!(UnicodePropertyEscape, strings) == 9usize); + assert!(offset_of!(UnicodePropertyEscape, name) == 16usize); + assert!(offset_of!(UnicodePropertyEscape, value) == 32usize); + + assert!(size_of::() == 8usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Dot, span) == 0usize); + + assert!(size_of::() == 48usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(CharacterClass, span) == 0usize); + assert!(offset_of!(CharacterClass, negative) == 8usize); + assert!(offset_of!(CharacterClass, kind) == 9usize); + assert!(offset_of!(CharacterClass, body) == 16usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 24usize); + assert!(align_of::() == 8usize); + + assert!(size_of::() == 40usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(CharacterClassRange, span) == 0usize); + assert!(offset_of!(CharacterClassRange, min) == 8usize); + assert!(offset_of!(CharacterClassRange, max) == 24usize); + + assert!(size_of::() == 48usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(ClassStringDisjunction, span) == 0usize); + assert!(offset_of!(ClassStringDisjunction, strings) == 8usize); + assert!(offset_of!(ClassStringDisjunction, body) == 16usize); + + assert!(size_of::() == 48usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(ClassString, span) == 0usize); + assert!(offset_of!(ClassString, strings) == 8usize); + assert!(offset_of!(ClassString, body) == 16usize); + + assert!(size_of::() == 64usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(CapturingGroup, span) == 0usize); + assert!(offset_of!(CapturingGroup, name) == 8usize); + assert!(offset_of!(CapturingGroup, body) == 24usize); + + assert!(size_of::() == 56usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(IgnoreGroup, span) == 0usize); + assert!(offset_of!(IgnoreGroup, enabling_modifiers) == 8usize); + assert!(offset_of!(IgnoreGroup, disabling_modifiers) == 11usize); + assert!(offset_of!(IgnoreGroup, body) == 16usize); + + assert!(size_of::() == 3usize); + assert!(align_of::() == 1usize); + assert!(offset_of!(ModifierFlags, ignore_case) == 0usize); + assert!(offset_of!(ModifierFlags, sticky) == 1usize); + assert!(offset_of!(ModifierFlags, multiline) == 2usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(IndexedReference, span) == 0usize); + assert!(offset_of!(IndexedReference, index) == 8usize); + + assert!(size_of::() == 24usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(NamedReference, span) == 0usize); + assert!(offset_of!(NamedReference, name) == 8usize); }; #[cfg(target_pointer_width = "32")] @@ -1431,16 +1587,19 @@ const _: () = { assert!(offset_of!(BigIntLiteral, raw) == 8usize); assert!(offset_of!(BigIntLiteral, base) == 16usize); - assert!(size_of::() == 20usize); + assert!(size_of::() == 24usize); assert!(align_of::() == 4usize); assert!(offset_of!(RegExpLiteral, span) == 0usize); assert!(offset_of!(RegExpLiteral, value) == 8usize); assert!(offset_of!(RegExpLiteral, regex) == 8usize); - assert!(size_of::() == 12usize); + assert!(size_of::() == 16usize); assert!(align_of::() == 4usize); assert!(offset_of!(RegExp, pattern) == 0usize); - assert!(offset_of!(RegExp, flags) == 8usize); + assert!(offset_of!(RegExp, flags) == 12usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); assert!(size_of::() == 0usize); assert!(align_of::() == 1usize); @@ -2806,6 +2965,156 @@ const _: () = { assert!(size_of::() == 1usize); assert!(align_of::() == 1usize); + + assert!(size_of::() == 56usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(RegularExpression, span) == 0usize); + assert!(offset_of!(RegularExpression, pattern) == 8usize); + assert!(offset_of!(RegularExpression, flags) == 40usize); + + assert!(size_of::() == 16usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Flags, span) == 0usize); + assert!(offset_of!(Flags, global) == 8usize); + assert!(offset_of!(Flags, ignore_case) == 9usize); + assert!(offset_of!(Flags, multiline) == 10usize); + assert!(offset_of!(Flags, unicode) == 11usize); + assert!(offset_of!(Flags, sticky) == 12usize); + assert!(offset_of!(Flags, dot_all) == 13usize); + assert!(offset_of!(Flags, has_indices) == 14usize); + assert!(offset_of!(Flags, unicode_sets) == 15usize); + + assert!(size_of::() == 32usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Pattern, span) == 0usize); + assert!(offset_of!(Pattern, body) == 8usize); + + assert!(size_of::() == 24usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Disjunction, span) == 0usize); + assert!(offset_of!(Disjunction, body) == 8usize); + + assert!(size_of::() == 24usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Alternative, span) == 0usize); + assert!(offset_of!(Alternative, body) == 8usize); + + assert!(size_of::() == 20usize); + assert!(align_of::() == 4usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(BoundaryAssertion, span) == 0usize); + assert!(offset_of!(BoundaryAssertion, kind) == 8usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 36usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(LookAroundAssertion, span) == 0usize); + assert!(offset_of!(LookAroundAssertion, kind) == 8usize); + assert!(offset_of!(LookAroundAssertion, body) == 12usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 56usize); + assert!(align_of::() == 8usize); + assert!(offset_of!(Quantifier, span) == 0usize); + assert!(offset_of!(Quantifier, min) == 8usize); + assert!(offset_of!(Quantifier, max) == 16usize); + assert!(offset_of!(Quantifier, greedy) == 32usize); + assert!(offset_of!(Quantifier, body) == 36usize); + + assert!(size_of::() == 16usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Character, span) == 0usize); + assert!(offset_of!(Character, kind) == 8usize); + assert!(offset_of!(Character, value) == 12usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(CharacterClassEscape, span) == 0usize); + assert!(offset_of!(CharacterClassEscape, kind) == 8usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 28usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(UnicodePropertyEscape, span) == 0usize); + assert!(offset_of!(UnicodePropertyEscape, negative) == 8usize); + assert!(offset_of!(UnicodePropertyEscape, strings) == 9usize); + assert!(offset_of!(UnicodePropertyEscape, name) == 12usize); + assert!(offset_of!(UnicodePropertyEscape, value) == 20usize); + + assert!(size_of::() == 8usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(Dot, span) == 0usize); + + assert!(size_of::() == 28usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(CharacterClass, span) == 0usize); + assert!(offset_of!(CharacterClass, negative) == 8usize); + assert!(offset_of!(CharacterClass, kind) == 9usize); + assert!(offset_of!(CharacterClass, body) == 12usize); + + assert!(size_of::() == 1usize); + assert!(align_of::() == 1usize); + + assert!(size_of::() == 20usize); + assert!(align_of::() == 4usize); + + assert!(size_of::() == 40usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(CharacterClassRange, span) == 0usize); + assert!(offset_of!(CharacterClassRange, min) == 8usize); + assert!(offset_of!(CharacterClassRange, max) == 24usize); + + assert!(size_of::() == 28usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(ClassStringDisjunction, span) == 0usize); + assert!(offset_of!(ClassStringDisjunction, strings) == 8usize); + assert!(offset_of!(ClassStringDisjunction, body) == 12usize); + + assert!(size_of::() == 28usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(ClassString, span) == 0usize); + assert!(offset_of!(ClassString, strings) == 8usize); + assert!(offset_of!(ClassString, body) == 12usize); + + assert!(size_of::() == 40usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(CapturingGroup, span) == 0usize); + assert!(offset_of!(CapturingGroup, name) == 8usize); + assert!(offset_of!(CapturingGroup, body) == 16usize); + + assert!(size_of::() == 40usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(IgnoreGroup, span) == 0usize); + assert!(offset_of!(IgnoreGroup, enabling_modifiers) == 8usize); + assert!(offset_of!(IgnoreGroup, disabling_modifiers) == 11usize); + assert!(offset_of!(IgnoreGroup, body) == 16usize); + + assert!(size_of::() == 3usize); + assert!(align_of::() == 1usize); + assert!(offset_of!(ModifierFlags, ignore_case) == 0usize); + assert!(offset_of!(ModifierFlags, sticky) == 1usize); + assert!(offset_of!(ModifierFlags, multiline) == 2usize); + + assert!(size_of::() == 12usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(IndexedReference, span) == 0usize); + assert!(offset_of!(IndexedReference, index) == 8usize); + + assert!(size_of::() == 16usize); + assert!(align_of::() == 4usize); + assert!(offset_of!(NamedReference, span) == 0usize); + assert!(offset_of!(NamedReference, name) == 8usize); }; #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))] diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index c76df853ebbba..4716bf8d25621 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -75,6 +75,17 @@ impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for RegExp<'old_alloc> { } } +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for RegExpPattern<'old_alloc> { + type Cloned = RegExpPattern<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + match self { + Self::Raw(it) => RegExpPattern::Raw(it.clone_in(allocator)), + Self::Invalid(it) => RegExpPattern::Invalid(it.clone_in(allocator)), + Self::Pattern(it) => RegExpPattern::Pattern(it.clone_in(allocator)), + } + } +} + impl<'alloc> CloneIn<'alloc> for EmptyObject { type Cloned = EmptyObject; fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index bfe5c60c51eb2..0d474511f1659 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1234,14 +1234,15 @@ impl<'a> Gen for RegExpLiteral<'a> { fn gen(&self, p: &mut Codegen, _ctx: Context) { p.add_source_mapping(self.span.start); let last = p.peek_nth(0); + let pattern_text = self.regex.pattern.source_text(p.source_text); // Avoid forming a single-line comment or "(&self, node: &AstNode<'a>, context: &LintContext<'a>) { if let Some(RegexPatternData { pattern, flags, span }) = regex_pattern(node) { let mut violations: Vec<&str> = Vec::new(); - - for matched_ctl_pattern in control_patterns(pattern) { + let pattern = pattern.as_ref(); + let pattern_text = pattern.source_text(context.source_text()); + for matched_ctl_pattern in control_patterns(pattern_text) { let ctl = matched_ctl_pattern.as_str(); // check for an even number of backslashes, since these will // prevent the pattern from being a control sequence if ctl.starts_with('\\') && matched_ctl_pattern.start() > 0 { - let pattern_chars: Vec = pattern.chars().collect(); // ew + let pattern_chars: Vec = pattern_text.chars().collect(); // ew // Convert byte index to char index let byte_start = matched_ctl_pattern.start(); - let char_start = pattern[..byte_start].chars().count(); + let char_start = pattern_text[..byte_start].chars().count(); let mut first_backslash = char_start; while first_backslash > 0 && pattern_chars[first_backslash] == '\\' { @@ -136,10 +137,25 @@ impl Rule for NoControlRegex { } } +/// Since we don't implement `ToOwned` trait. +enum PatRef<'a> { + Owned(RegExpPattern<'a>), + Borrowed(&'a RegExpPattern<'a>), +} + +impl<'a> AsRef> for PatRef<'a> { + fn as_ref(&self) -> &RegExpPattern<'a> { + match self { + Self::Owned(pat) => pat, + Self::Borrowed(pat) => pat, + } + } +} + struct RegexPatternData<'a> { /// A regex pattern, either from a literal (`/foo/`) a RegExp constructor /// (`new RegExp("foo")`), or a RegExp function call (`RegExp("foo")) - pattern: &'a str, + pattern: PatRef<'a>, /// Regex flags, if found. It's possible for this to be `Some` but have /// no flags. /// @@ -166,7 +182,7 @@ fn regex_pattern<'a>(node: &AstNode<'a>) -> Option> { match kind { // regex literal AstKind::RegExpLiteral(reg) => Some(RegexPatternData { - pattern: reg.regex.pattern.as_ref(), + pattern: PatRef::Borrowed(®.regex.pattern), flags: Some(reg.regex.flags), span: reg.span, }), @@ -187,7 +203,7 @@ fn regex_pattern<'a>(node: &AstNode<'a>) -> Option> { // Note that we're intentionally reporting the entire "new // RegExp("pat") expression, not just "pat". Some(RegexPatternData { - pattern: pattern.value.as_ref(), + pattern: PatRef::Owned(RegExpPattern::Raw(pattern.value.as_ref())), flags: extract_regex_flags(&expr.arguments), span: kind.span(), }) @@ -215,7 +231,7 @@ fn regex_pattern<'a>(node: &AstNode<'a>) -> Option> { // Note that we're intentionally reporting the entire "new // RegExp("pat") expression, not just "pat". Some(RegexPatternData { - pattern: pattern.value.as_ref(), + pattern: PatRef::Owned(RegExpPattern::Raw(pattern.value.as_ref())), flags: extract_regex_flags(&expr.arguments), span: kind.span(), }) diff --git a/crates/oxc_linter/src/rules/eslint/no_div_regex.rs b/crates/oxc_linter/src/rules/eslint/no_div_regex.rs index 04b2e2bf6d89b..44f0e7f9d9eef 100644 --- a/crates/oxc_linter/src/rules/eslint/no_div_regex.rs +++ b/crates/oxc_linter/src/rules/eslint/no_div_regex.rs @@ -36,7 +36,7 @@ declare_oxc_lint!( impl Rule for NoDivRegex { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::RegExpLiteral(lit) = node.kind() { - if lit.regex.pattern.starts_with('=') { + if lit.regex.pattern.source_text(ctx.source_text()).starts_with('=') { ctx.diagnostic_with_fix(no_div_regex_diagnostic(lit.span), |fixer| { let span = Span::sized(lit.span.start + 1, 1); fixer.replace(span, "[=]") diff --git a/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs b/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs index 738086db046f9..203d1a1ad6cef 100644 --- a/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs +++ b/crates/oxc_linter/src/rules/eslint/no_empty_character_class.rs @@ -49,7 +49,9 @@ impl Rule for NoEmptyCharacterClass { } if let AstKind::RegExpLiteral(lit) = node.kind() { - if !NO_EMPTY_CLASS_REGEX_PATTERN.is_match(&lit.regex.pattern) { + if !NO_EMPTY_CLASS_REGEX_PATTERN + .is_match(lit.regex.pattern.source_text(ctx.source_text())) + { ctx.diagnostic(no_empty_character_class_diagnostic(lit.span)); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs b/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs index 8f25eb7e02426..3e37ac8910902 100644 --- a/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs +++ b/crates/oxc_linter/src/rules/eslint/no_regex_spaces.rs @@ -44,7 +44,7 @@ impl Rule for NoRegexSpaces { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { AstKind::RegExpLiteral(lit) => { - if let Some(span) = Self::find_literal_to_report(lit) { + if let Some(span) = Self::find_literal_to_report(lit, ctx) { ctx.diagnostic(no_regex_spaces_diagnostic(span)); // /a b/ } } @@ -67,14 +67,13 @@ impl Rule for NoRegexSpaces { } impl NoRegexSpaces { - fn find_literal_to_report(literal: &RegExpLiteral) -> Option { - if Self::has_exempted_char_class(&literal.regex.pattern) { + fn find_literal_to_report(literal: &RegExpLiteral, ctx: &LintContext) -> Option { + let pattern_text = literal.regex.pattern.source_text(ctx.source_text()); + if Self::has_exempted_char_class(pattern_text) { return None; } - if let Some((idx_start, idx_end)) = - Self::find_consecutive_spaces_indices(&literal.regex.pattern) - { + if let Some((idx_start, idx_end)) = Self::find_consecutive_spaces_indices(pattern_text) { let start = literal.span.start + u32::try_from(idx_start).unwrap() + 1; let end = literal.span.start + u32::try_from(idx_end).unwrap() + 2; diff --git a/crates/oxc_linter/src/rules/unicorn/no_hex_escape.rs b/crates/oxc_linter/src/rules/unicorn/no_hex_escape.rs index 42bcd57310429..198df27a8a6c7 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_hex_escape.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_hex_escape.rs @@ -85,7 +85,9 @@ impl Rule for NoHexEscape { }); } AstKind::RegExpLiteral(regex) => { - if let Some(fixed) = check_escape(®ex.regex.pattern) { + if let Some(fixed) = + check_escape(regex.regex.pattern.source_text(ctx.source_text())) + { #[allow(clippy::cast_possible_truncation)] ctx.diagnostic_with_fix(no_hex_escape_diagnostic(regex.span), |fixer| { fixer.replace( diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs index b0d866639389e..ca3f29ef3cd10 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs @@ -78,7 +78,7 @@ impl Rule for PreferStringReplaceAll { let pattern = &call_expr.arguments[0]; match method_name_str { "replaceAll" => { - if let Some(k) = get_pattern_replacement(pattern) { + if let Some(k) = get_pattern_replacement(pattern, ctx) { ctx.diagnostic_with_fix(string_literal(pattern.span(), &k), |fixer| { // foo.replaceAll(/hello world/g, bar) => foo.replaceAll("hello world", bar) fixer.replace(pattern.span(), format!("{k:?}")) @@ -114,7 +114,10 @@ fn is_reg_exp_with_global_flag<'a>(expr: &'a Argument<'a>) -> bool { false } -fn get_pattern_replacement<'a>(expr: &'a Argument<'a>) -> Option { +fn get_pattern_replacement<'a>( + expr: &'a Argument<'a>, + ctx: &LintContext<'a>, +) -> Option { let Argument::RegExpLiteral(reg_exp_literal) = expr else { return None; }; @@ -123,11 +126,12 @@ fn get_pattern_replacement<'a>(expr: &'a Argument<'a>) -> Option { return None; } - if !is_simple_string(®_exp_literal.regex.pattern) { + let pattern_text = reg_exp_literal.regex.pattern.source_text(ctx.source_text()); + if !is_simple_string(pattern_text) { return None; } - Some(reg_exp_literal.regex.pattern.to_compact_str()) + Some(CompactStr::new(pattern_text)) } fn is_simple_string(str: &str) -> bool { diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_starts_ends_with.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_starts_ends_with.rs index 4b458019c745f..9714bdd036196 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_starts_ends_with.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_starts_ends_with.rs @@ -74,19 +74,21 @@ impl Rule for PreferStringStartsEndsWith { return; }; - let Some(err_kind) = check_regex(regex) else { + let pattern_text = regex.regex.pattern.source_text(ctx.source_text()); + + let Some(err_kind) = check_regex(regex, pattern_text) else { return; }; match err_kind { ErrorKind::StartsWith => { ctx.diagnostic_with_fix(starts_with(member_expr.span()), |fixer| { - do_fix(fixer, err_kind, call_expr, regex) + do_fix(fixer, err_kind, call_expr, pattern_text) }); } ErrorKind::EndsWith => { ctx.diagnostic_with_fix(ends_with(member_expr.span()), |fixer| { - do_fix(fixer, err_kind, call_expr, regex) + do_fix(fixer, err_kind, call_expr, pattern_text) }); } } @@ -97,13 +99,12 @@ fn do_fix<'a>( fixer: RuleFixer<'_, 'a>, err_kind: ErrorKind, call_expr: &CallExpression<'a>, - regex: &RegExpLiteral, + pattern_text: &str, ) -> RuleFix<'a> { let Some(target_span) = can_replace(call_expr) else { return fixer.noop() }; - let pattern = ®ex.regex.pattern; let (argument, method) = match err_kind { - ErrorKind::StartsWith => (pattern.trim_start_matches('^'), "startsWith"), - ErrorKind::EndsWith => (pattern.trim_end_matches('$'), "endsWith"), + ErrorKind::StartsWith => (pattern_text.trim_start_matches('^'), "startsWith"), + ErrorKind::EndsWith => (pattern_text.trim_end_matches('$'), "endsWith"), }; let fix_text = format!(r#"{}.{}("{}")"#, fixer.source_range(target_span), method, argument); @@ -133,24 +134,22 @@ enum ErrorKind { EndsWith, } -fn check_regex(regexp_lit: &RegExpLiteral) -> Option { +fn check_regex(regexp_lit: &RegExpLiteral, pattern_text: &str) -> Option { if regexp_lit.regex.flags.intersects(RegExpFlags::M) || (regexp_lit.regex.flags.intersects(RegExpFlags::I | RegExpFlags::M) - && is_useless_case_sensitive_regex_flag(regexp_lit)) + && is_useless_case_sensitive_regex_flag(pattern_text)) { return None; } - if regexp_lit.regex.pattern.starts_with('^') - && is_simple_string(®exp_lit.regex.pattern.as_str()[1..regexp_lit.regex.pattern.len()]) + if pattern_text.starts_with('^') + && is_simple_string(&pattern_text[1..regexp_lit.regex.pattern.len()]) { return Some(ErrorKind::StartsWith); } - if regexp_lit.regex.pattern.ends_with('$') - && is_simple_string( - ®exp_lit.regex.pattern.as_str()[0..regexp_lit.regex.pattern.len() - 1], - ) + if pattern_text.ends_with('$') + && is_simple_string(&pattern_text[0..regexp_lit.regex.pattern.len() - 1]) { return Some(ErrorKind::EndsWith); } @@ -165,9 +164,9 @@ fn is_simple_string(str: &str) -> bool { // `/^#/i` => `true` (the `i` flag is useless) // `/^foo/i` => `false` (the `i` flag is not useless) -fn is_useless_case_sensitive_regex_flag(regexp_lit: &RegExpLiteral) -> bool { +fn is_useless_case_sensitive_regex_flag(pattern_text: &str) -> bool { // ignore `^` and `$` (start and end of string) - let pat = regexp_lit.regex.pattern.trim_start_matches('^').trim_end_matches('$'); + let pat = pattern_text.trim_start_matches('^').trim_end_matches('$'); pat.chars().any(|c| c.is_ascii_alphabetic()) } diff --git a/crates/oxc_linter/src/utils/unicorn.rs b/crates/oxc_linter/src/utils/unicorn.rs index 204f2f840108a..3806039ddb34e 100644 --- a/crates/oxc_linter/src/utils/unicorn.rs +++ b/crates/oxc_linter/src/utils/unicorn.rs @@ -181,7 +181,8 @@ pub fn is_same_reference(left: &Expression, right: &Expression, ctx: &LintContex return left_num.raw == right_num.raw; } (Expression::RegExpLiteral(left_regexp), Expression::RegExpLiteral(right_regexp)) => { - return left_regexp.regex.pattern == right_regexp.regex.pattern + return left_regexp.regex.pattern.source_text(ctx.source_text()) + == right_regexp.regex.pattern.source_text(ctx.source_text()) && left_regexp.regex.flags == right_regexp.regex.flags; } (Expression::BooleanLiteral(left_bool), Expression::BooleanLiteral(right_bool)) => { diff --git a/crates/oxc_parser/examples/regular_expression.rs b/crates/oxc_parser/examples/regular_expression.rs index 52c26d107378c..a26225afb24cc 100644 --- a/crates/oxc_parser/examples/regular_expression.rs +++ b/crates/oxc_parser/examples/regular_expression.rs @@ -3,7 +3,7 @@ use std::{env, fs, path::Path, sync::Arc}; use oxc_allocator::Allocator; use oxc_ast::{ast, AstKind, Visit}; -use oxc_parser::Parser; +use oxc_parser::{ParseOptions, Parser}; use oxc_regular_expression::{FlagsParser, ParserOptions, PatternParser}; use oxc_span::SourceType; @@ -18,8 +18,10 @@ fn main() { let source_type = SourceType::from_path(path).unwrap(); let allocator = Allocator::default(); + let options = ParseOptions { parse_regular_expression: true, ..ParseOptions::default() }; - let parser_ret = Parser::new(&allocator, source_text.as_ref(), source_type).parse(); + let parser_ret = + Parser::new(&allocator, source_text.as_ref(), source_type).with_options(options).parse(); if !parser_ret.errors.is_empty() { println!("Parsing failed:"); for error in parser_ret.errors { @@ -47,24 +49,7 @@ impl<'a> Visit<'a> for RegularExpressionVisitor { AstKind::RegExpLiteral(re) => { println!("🍀 {}", re.span.source_text(self.source_text.as_ref())); - let parsed = PatternParser::new( - &allocator, - re.regex.pattern.as_str(), - ParserOptions { - span_offset: re.span.start + 1, - unicode_mode: re.regex.flags.contains(ast::RegExpFlags::U) - || re.regex.flags.contains(ast::RegExpFlags::V), - unicode_sets_mode: re.regex.flags.contains(ast::RegExpFlags::V), - }, - ) - .parse(); - - if let Err(error) = parsed { - let error = error.with_source_code(Arc::clone(&self.source_text)); - println!("{error:?}"); - return; - } - println!("{parsed:#?}"); + println!("{re:#?}"); println!(); } AstKind::NewExpression(new_expr) diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 86c806c267d90..18fb30e8fd85a 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -341,19 +341,22 @@ impl<'a> ParserImpl<'a> { // split out pattern let (pattern_end, flags) = self.read_regex()?; let pattern_start = self.cur_token().start + 1; // +1 to exclude `/` - let pattern = &self.source_text[pattern_start as usize..pattern_end as usize]; + let pattern_text = &self.source_text[pattern_start as usize..pattern_end as usize]; self.bump_any(); - let _pattern = self + let pattern = self .options .parse_regular_expression - .then(|| self.parse_regex_pattern(pattern_start, pattern, flags)); - - Ok(self.ast.reg_exp_literal( - self.end_span(span), - EmptyObject, - RegExp { pattern: self.ast.atom(pattern), flags }, - )) + .then_some(()) + .map(|()| self.parse_regex_pattern(pattern_start, pattern_text, flags)) + .map_or_else( + || RegExpPattern::Raw(pattern_text), + |pat| { + pat.map_or_else(|| RegExpPattern::Invalid(pattern_text), RegExpPattern::Pattern) + }, + ); + + Ok(self.ast.reg_exp_literal(self.end_span(span), EmptyObject, RegExp { pattern, flags })) } fn parse_regex_pattern( @@ -361,7 +364,7 @@ impl<'a> ParserImpl<'a> { span_offset: u32, pattern: &'a str, flags: RegExpFlags, - ) -> Option> { + ) -> Option>> { use oxc_regular_expression::{ParserOptions, PatternParser}; let options = ParserOptions { span_offset, @@ -369,7 +372,7 @@ impl<'a> ParserImpl<'a> { unicode_sets_mode: flags.contains(RegExpFlags::V), }; match PatternParser::new(self.ast.allocator, pattern, options).parse() { - Ok(regular_expression) => Some(regular_expression), + Ok(regular_expression) => Some(self.ast.alloc(regular_expression)), Err(diagnostic) => { self.error(diagnostic); None diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 8e143014df93a..f421967853ccf 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -1416,7 +1416,7 @@ impl<'a> Format<'a> for RegExpLiteral<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); parts.push(ss!("/")); - parts.push(p.str(self.regex.pattern.as_str())); + parts.push(p.str(self.regex.pattern.source_text(p.source_text))); parts.push(ss!("/")); parts.push(format!(p, self.regex.flags)); Doc::Array(parts) diff --git a/crates/oxc_regular_expression/Cargo.toml b/crates/oxc_regular_expression/Cargo.toml index f25ddd899fae2..f148b0e3bbce1 100644 --- a/crates/oxc_regular_expression/Cargo.toml +++ b/crates/oxc_regular_expression/Cargo.toml @@ -23,7 +23,22 @@ doctest = false oxc_allocator = { workspace = true } oxc_diagnostics = { workspace = true } oxc_span = { workspace = true } +oxc_ast_macros = { workspace = true } phf = { workspace = true, features = ["macros"] } rustc-hash = { workspace = true } unicode-id-start = { workspace = true } +serde = { workspace = true, features = ["derive"], optional = true } + +tsify = { workspace = true, optional = true } +wasm-bindgen = { workspace = true, optional = true } + +[features] +default = [] +serialize = [ + "dep:serde", + "dep:tsify", + "dep:wasm-bindgen", + "oxc_allocator/serialize", + "oxc_span/serialize", +] diff --git a/crates/oxc_regular_expression/src/ast.rs b/crates/oxc_regular_expression/src/ast.rs index edd8405ba3d54..d142180260df2 100644 --- a/crates/oxc_regular_expression/src/ast.rs +++ b/crates/oxc_regular_expression/src/ast.rs @@ -1,14 +1,33 @@ -use oxc_allocator::{Box, Vec}; -use oxc_span::{Atom as SpanAtom, Span}; +// NB: `#[span]`, `#[scope(...)]`,`#[visit(...)]` and `#[generate_derive(...)]` do NOT do anything to the code. +// They are purely markers for codegen used in `tasks/ast_tools` and `crates/oxc_traverse/scripts`. See docs in those crates. +// Read [`macro@oxc_ast_macros::ast`] for more information. -#[derive(Debug)] +// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]` +#![allow(non_snake_case)] + +use oxc_allocator::{Box, CloneIn, Vec}; +use oxc_ast_macros::ast; +use oxc_span::{Atom, Span}; + +#[cfg(feature = "serialize")] +use serde::Serialize; +#[cfg(feature = "serialize")] +use tsify::Tsify; + +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct RegularExpression<'a> { pub span: Span, pub pattern: Pattern<'a>, pub flags: Flags, } -#[derive(Debug)] +#[ast] +#[derive(Debug, Clone, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Flags { pub span: Span, pub global: bool, @@ -22,80 +41,109 @@ pub struct Flags { } /// The root of the `PatternParser` result. -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Pattern<'a> { pub span: Span, pub body: Disjunction<'a>, } /// Pile of [`Alternative`]s separated by `|`. -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Disjunction<'a> { pub span: Span, pub body: Vec<'a, Alternative<'a>>, } /// Single unit of `|` separated alternatives. -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Alternative<'a> { pub span: Span, pub body: Vec<'a, Term<'a>>, } /// Single unit of [`Alternative`], containing various kinds. -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum Term<'a> { // Assertion, QuantifiableAssertion - BoundaryAssertion(BoundaryAssertion), - LookAroundAssertion(Box<'a, LookAroundAssertion<'a>>), + BoundaryAssertion(BoundaryAssertion) = 0, + LookAroundAssertion(Box<'a, LookAroundAssertion<'a>>) = 1, // Quantifier - Quantifier(Box<'a, Quantifier<'a>>), + Quantifier(Box<'a, Quantifier<'a>>) = 2, // Atom, ExtendedAtom - Character(Character), - Dot(Dot), - CharacterClassEscape(CharacterClassEscape), - UnicodePropertyEscape(Box<'a, UnicodePropertyEscape<'a>>), - CharacterClass(Box<'a, CharacterClass<'a>>), - CapturingGroup(Box<'a, CapturingGroup<'a>>), - IgnoreGroup(Box<'a, IgnoreGroup<'a>>), - IndexedReference(IndexedReference), - NamedReference(Box<'a, NamedReference<'a>>), + Character(Character) = 3, + Dot(Dot) = 4, + CharacterClassEscape(CharacterClassEscape) = 5, + UnicodePropertyEscape(Box<'a, UnicodePropertyEscape<'a>>) = 6, + CharacterClass(Box<'a, CharacterClass<'a>>) = 7, + CapturingGroup(Box<'a, CapturingGroup<'a>>) = 8, + IgnoreGroup(Box<'a, IgnoreGroup<'a>>) = 9, + IndexedReference(IndexedReference) = 10, + NamedReference(Box<'a, NamedReference<'a>>) = 11, } /// Simple form of assertion. /// e.g. `^`, `$`, `\b`, `\B` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct BoundaryAssertion { pub span: Span, pub kind: BoundaryAssertionKind, } -#[derive(Debug)] + +#[ast] +#[derive(Debug, Clone, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum BoundaryAssertionKind { - Start, - End, - Boundary, - NegativeBoundary, + Start = 0, + End = 1, + Boundary = 2, + NegativeBoundary = 3, } /// Lookaround assertion. /// e.g. `(?=...)`, `(?!...)`, `(?<=...)`, `(? { pub span: Span, pub kind: LookAroundAssertionKind, pub body: Disjunction<'a>, } -#[derive(Debug)] + +#[ast] +#[derive(Debug, Clone, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum LookAroundAssertionKind { - Lookahead, - NegativeLookahead, - Lookbehind, - NegativeLookbehind, + Lookahead = 0, + NegativeLookahead = 1, + Lookbehind = 2, + NegativeLookbehind = 3, } /// Quantifier holding a [`Term`] and its repetition count. /// e.g. `a*`, `b+`, `c?`, `d{3}`, `e{4,}`, `f{5,6}` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Quantifier<'a> { pub span: Span, pub min: u64, @@ -106,7 +154,10 @@ pub struct Quantifier<'a> { } /// Single character. -#[derive(Debug, Copy, Clone)] +#[ast] +#[derive(Debug, Clone, Copy, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Character { /// This will be invalid position when `UnicodeMode` is disabled and `value` is a surrogate pair. pub span: Span, @@ -115,57 +166,75 @@ pub struct Character { pub value: u32, } -#[derive(Debug, Copy, Clone)] +#[ast] +#[derive(Debug, Clone, Copy, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum CharacterKind { - ControlLetter, - HexadecimalEscape, - Identifier, - Null, - Octal, - SingleEscape, - Symbol, - UnicodeEscape, + ControlLetter = 0, + HexadecimalEscape = 1, + Identifier = 2, + Null = 3, + Octal = 4, + SingleEscape = 5, + Symbol = 6, + UnicodeEscape = 7, } /// Character class. /// e.g. `\d`, `\D`, `\s`, `\S`, `\w`, `\W` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct CharacterClassEscape { pub span: Span, pub kind: CharacterClassEscapeKind, } -#[derive(Debug)] +#[ast] +#[derive(Debug, Clone, Copy, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum CharacterClassEscapeKind { - D, - NegativeD, - S, - NegativeS, - W, - NegativeW, + D = 0, + NegativeD = 1, + S = 2, + NegativeS = 3, + W = 4, + NegativeW = 5, } /// Unicode property. /// e.g. `\p{ASCII}`, `\P{ASCII}`, `\p{sc=Hiragana}`, `\P{sc=Hiragana}` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct UnicodePropertyEscape<'a> { pub span: Span, pub negative: bool, /// `true` if `UnicodeSetsMode` and `name` matched unicode property of strings. pub strings: bool, - pub name: SpanAtom<'a>, - pub value: Option>, + pub name: Atom<'a>, + pub value: Option>, } /// The `.`. -#[derive(Debug)] +#[ast] +#[derive(Debug, Clone, Copy, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct Dot { pub span: Span, } /// Character class wrapped by `[]`. /// e.g. `[a-z]`, `[^A-Z]`, `[abc]`, `[a&&b&&c]`, `[[a-z]--x--y]` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct CharacterClass<'a> { pub span: Span, pub negative: bool, @@ -173,30 +242,39 @@ pub struct CharacterClass<'a> { pub body: Vec<'a, CharacterClassContents<'a>>, } -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum CharacterClassContentsKind { - Union, + Union = 0, /// `UnicodeSetsMode` only. - Intersection, + Intersection = 1, /// `UnicodeSetsMode` only. - Subtraction, + Subtraction = 2, } -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub enum CharacterClassContents<'a> { - CharacterClassRange(Box<'a, CharacterClassRange>), - CharacterClassEscape(CharacterClassEscape), - UnicodePropertyEscape(Box<'a, UnicodePropertyEscape<'a>>), - Character(Character), + CharacterClassRange(Box<'a, CharacterClassRange>) = 0, + CharacterClassEscape(CharacterClassEscape) = 1, + UnicodePropertyEscape(Box<'a, UnicodePropertyEscape<'a>>) = 2, + Character(Character) = 3, /// `UnicodeSetsMode` only - NestedCharacterClass(Box<'a, CharacterClass<'a>>), + NestedCharacterClass(Box<'a, CharacterClass<'a>>) = 4, /// `UnicodeSetsMode` only - ClassStringDisjunction(Box<'a, ClassStringDisjunction<'a>>), + ClassStringDisjunction(Box<'a, ClassStringDisjunction<'a>>) = 5, } /// `-` separated range of characters. /// e.g. `a-z`, `A-Z`, `0-9` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct CharacterClassRange { pub span: Span, pub min: Character, @@ -204,7 +282,10 @@ pub struct CharacterClassRange { } /// `|` separated string of characters wrapped by `\q{}`. -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct ClassStringDisjunction<'a> { pub span: Span, /// `true` if body is empty or contain [`ClassString`] which `strings` is `true` @@ -213,7 +294,10 @@ pub struct ClassStringDisjunction<'a> { } /// Single unit of [`ClassStringDisjunction`]. -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct ClassString<'a> { pub span: Span, /// `true` if body is empty or contain 2 more characters. @@ -223,16 +307,22 @@ pub struct ClassString<'a> { /// Named or unnamed capturing group. /// e.g. `(...)`, `(?...)` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct CapturingGroup<'a> { pub span: Span, - pub name: Option>, + pub name: Option>, pub body: Disjunction<'a>, } /// Pseudo-group for ignoring. /// e.g. `(?:...)` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct IgnoreGroup<'a> { pub span: Span, pub enabling_modifiers: Option, @@ -240,7 +330,10 @@ pub struct IgnoreGroup<'a> { pub body: Disjunction<'a>, } -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct ModifierFlags { pub ignore_case: bool, pub sticky: bool, @@ -249,7 +342,10 @@ pub struct ModifierFlags { /// Backreference by index. /// e.g. `\1`, `\2`, `\3` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct IndexedReference { pub span: Span, pub index: u32, @@ -257,8 +353,11 @@ pub struct IndexedReference { /// Backreference by name. /// e.g. `\k` -#[derive(Debug)] +#[ast] +#[derive(Debug, Hash)] +#[generate_derive(CloneIn)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] pub struct NamedReference<'a> { pub span: Span, - pub name: SpanAtom<'a>, + pub name: Atom<'a>, } diff --git a/crates/oxc_regular_expression/src/generated/derive_clone_in.rs b/crates/oxc_regular_expression/src/generated/derive_clone_in.rs new file mode 100644 index 0000000000000..674fc2c607483 --- /dev/null +++ b/crates/oxc_regular_expression/src/generated/derive_clone_in.rs @@ -0,0 +1,338 @@ +// Auto-generated code, DO NOT EDIT DIRECTLY! +// To edit this generated file you have to edit `tasks/ast_tools/src/derives/clone_in.rs` + +#![allow(clippy::default_trait_access)] + +use oxc_allocator::{Allocator, CloneIn}; + +#[allow(clippy::wildcard_imports)] +use crate::ast::*; + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for RegularExpression<'old_alloc> { + type Cloned = RegularExpression<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + RegularExpression { + span: self.span.clone_in(allocator), + pattern: self.pattern.clone_in(allocator), + flags: self.flags.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for Flags { + type Cloned = Flags; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + Flags { + span: self.span.clone_in(allocator), + global: self.global.clone_in(allocator), + ignore_case: self.ignore_case.clone_in(allocator), + multiline: self.multiline.clone_in(allocator), + unicode: self.unicode.clone_in(allocator), + sticky: self.sticky.clone_in(allocator), + dot_all: self.dot_all.clone_in(allocator), + has_indices: self.has_indices.clone_in(allocator), + unicode_sets: self.unicode_sets.clone_in(allocator), + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Pattern<'old_alloc> { + type Cloned = Pattern<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + Pattern { span: self.span.clone_in(allocator), body: self.body.clone_in(allocator) } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Disjunction<'old_alloc> { + type Cloned = Disjunction<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + Disjunction { span: self.span.clone_in(allocator), body: self.body.clone_in(allocator) } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Alternative<'old_alloc> { + type Cloned = Alternative<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + Alternative { span: self.span.clone_in(allocator), body: self.body.clone_in(allocator) } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Term<'old_alloc> { + type Cloned = Term<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + match self { + Self::BoundaryAssertion(it) => Term::BoundaryAssertion(it.clone_in(allocator)), + Self::LookAroundAssertion(it) => Term::LookAroundAssertion(it.clone_in(allocator)), + Self::Quantifier(it) => Term::Quantifier(it.clone_in(allocator)), + Self::Character(it) => Term::Character(it.clone_in(allocator)), + Self::Dot(it) => Term::Dot(it.clone_in(allocator)), + Self::CharacterClassEscape(it) => Term::CharacterClassEscape(it.clone_in(allocator)), + Self::UnicodePropertyEscape(it) => Term::UnicodePropertyEscape(it.clone_in(allocator)), + Self::CharacterClass(it) => Term::CharacterClass(it.clone_in(allocator)), + Self::CapturingGroup(it) => Term::CapturingGroup(it.clone_in(allocator)), + Self::IgnoreGroup(it) => Term::IgnoreGroup(it.clone_in(allocator)), + Self::IndexedReference(it) => Term::IndexedReference(it.clone_in(allocator)), + Self::NamedReference(it) => Term::NamedReference(it.clone_in(allocator)), + } + } +} + +impl<'alloc> CloneIn<'alloc> for BoundaryAssertion { + type Cloned = BoundaryAssertion; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + BoundaryAssertion { + span: self.span.clone_in(allocator), + kind: self.kind.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for BoundaryAssertionKind { + type Cloned = BoundaryAssertionKind; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + match self { + Self::Start => BoundaryAssertionKind::Start, + Self::End => BoundaryAssertionKind::End, + Self::Boundary => BoundaryAssertionKind::Boundary, + Self::NegativeBoundary => BoundaryAssertionKind::NegativeBoundary, + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for LookAroundAssertion<'old_alloc> { + type Cloned = LookAroundAssertion<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + LookAroundAssertion { + span: self.span.clone_in(allocator), + kind: self.kind.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for LookAroundAssertionKind { + type Cloned = LookAroundAssertionKind; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + match self { + Self::Lookahead => LookAroundAssertionKind::Lookahead, + Self::NegativeLookahead => LookAroundAssertionKind::NegativeLookahead, + Self::Lookbehind => LookAroundAssertionKind::Lookbehind, + Self::NegativeLookbehind => LookAroundAssertionKind::NegativeLookbehind, + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Quantifier<'old_alloc> { + type Cloned = Quantifier<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + Quantifier { + span: self.span.clone_in(allocator), + min: self.min.clone_in(allocator), + max: self.max.clone_in(allocator), + greedy: self.greedy.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for Character { + type Cloned = Character; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + Character { + span: self.span.clone_in(allocator), + kind: self.kind.clone_in(allocator), + value: self.value.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for CharacterKind { + type Cloned = CharacterKind; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + match self { + Self::ControlLetter => CharacterKind::ControlLetter, + Self::HexadecimalEscape => CharacterKind::HexadecimalEscape, + Self::Identifier => CharacterKind::Identifier, + Self::Null => CharacterKind::Null, + Self::Octal => CharacterKind::Octal, + Self::SingleEscape => CharacterKind::SingleEscape, + Self::Symbol => CharacterKind::Symbol, + Self::UnicodeEscape => CharacterKind::UnicodeEscape, + } + } +} + +impl<'alloc> CloneIn<'alloc> for CharacterClassEscape { + type Cloned = CharacterClassEscape; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + CharacterClassEscape { + span: self.span.clone_in(allocator), + kind: self.kind.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for CharacterClassEscapeKind { + type Cloned = CharacterClassEscapeKind; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + match self { + Self::D => CharacterClassEscapeKind::D, + Self::NegativeD => CharacterClassEscapeKind::NegativeD, + Self::S => CharacterClassEscapeKind::S, + Self::NegativeS => CharacterClassEscapeKind::NegativeS, + Self::W => CharacterClassEscapeKind::W, + Self::NegativeW => CharacterClassEscapeKind::NegativeW, + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for UnicodePropertyEscape<'old_alloc> { + type Cloned = UnicodePropertyEscape<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + UnicodePropertyEscape { + span: self.span.clone_in(allocator), + negative: self.negative.clone_in(allocator), + strings: self.strings.clone_in(allocator), + name: self.name.clone_in(allocator), + value: self.value.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for Dot { + type Cloned = Dot; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + Dot { span: self.span.clone_in(allocator) } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for CharacterClass<'old_alloc> { + type Cloned = CharacterClass<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + CharacterClass { + span: self.span.clone_in(allocator), + negative: self.negative.clone_in(allocator), + kind: self.kind.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for CharacterClassContentsKind { + type Cloned = CharacterClassContentsKind; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + match self { + Self::Union => CharacterClassContentsKind::Union, + Self::Intersection => CharacterClassContentsKind::Intersection, + Self::Subtraction => CharacterClassContentsKind::Subtraction, + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for CharacterClassContents<'old_alloc> { + type Cloned = CharacterClassContents<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + match self { + Self::CharacterClassRange(it) => { + CharacterClassContents::CharacterClassRange(it.clone_in(allocator)) + } + Self::CharacterClassEscape(it) => { + CharacterClassContents::CharacterClassEscape(it.clone_in(allocator)) + } + Self::UnicodePropertyEscape(it) => { + CharacterClassContents::UnicodePropertyEscape(it.clone_in(allocator)) + } + Self::Character(it) => CharacterClassContents::Character(it.clone_in(allocator)), + Self::NestedCharacterClass(it) => { + CharacterClassContents::NestedCharacterClass(it.clone_in(allocator)) + } + Self::ClassStringDisjunction(it) => { + CharacterClassContents::ClassStringDisjunction(it.clone_in(allocator)) + } + } + } +} + +impl<'alloc> CloneIn<'alloc> for CharacterClassRange { + type Cloned = CharacterClassRange; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + CharacterClassRange { + span: self.span.clone_in(allocator), + min: self.min.clone_in(allocator), + max: self.max.clone_in(allocator), + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for ClassStringDisjunction<'old_alloc> { + type Cloned = ClassStringDisjunction<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + ClassStringDisjunction { + span: self.span.clone_in(allocator), + strings: self.strings.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for ClassString<'old_alloc> { + type Cloned = ClassString<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + ClassString { + span: self.span.clone_in(allocator), + strings: self.strings.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for CapturingGroup<'old_alloc> { + type Cloned = CapturingGroup<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + CapturingGroup { + span: self.span.clone_in(allocator), + name: self.name.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for IgnoreGroup<'old_alloc> { + type Cloned = IgnoreGroup<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + IgnoreGroup { + span: self.span.clone_in(allocator), + enabling_modifiers: self.enabling_modifiers.clone_in(allocator), + disabling_modifiers: self.disabling_modifiers.clone_in(allocator), + body: self.body.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for ModifierFlags { + type Cloned = ModifierFlags; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + ModifierFlags { + ignore_case: self.ignore_case.clone_in(allocator), + sticky: self.sticky.clone_in(allocator), + multiline: self.multiline.clone_in(allocator), + } + } +} + +impl<'alloc> CloneIn<'alloc> for IndexedReference { + type Cloned = IndexedReference; + fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { + IndexedReference { + span: self.span.clone_in(allocator), + index: self.index.clone_in(allocator), + } + } +} + +impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for NamedReference<'old_alloc> { + type Cloned = NamedReference<'new_alloc>; + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + NamedReference { span: self.span.clone_in(allocator), name: self.name.clone_in(allocator) } + } +} diff --git a/crates/oxc_regular_expression/src/lib.rs b/crates/oxc_regular_expression/src/lib.rs index 1a4df8a4e9025..c6e172a3241bb 100644 --- a/crates/oxc_regular_expression/src/lib.rs +++ b/crates/oxc_regular_expression/src/lib.rs @@ -9,6 +9,10 @@ mod options; mod span; mod surroage_pair; +mod generated { + mod derive_clone_in; +} + pub use crate::body_parser::PatternParser; pub use crate::flag_parser::FlagsParser; pub use crate::literal_parser::Parser; diff --git a/tasks/ast_tools/src/generators/assert_layouts.rs b/tasks/ast_tools/src/generators/assert_layouts.rs index b30f8189506f6..f6cf2ffc01728 100644 --- a/tasks/ast_tools/src/generators/assert_layouts.rs +++ b/tasks/ast_tools/src/generators/assert_layouts.rs @@ -40,6 +40,10 @@ impl Generator for AssertLayouts { #[allow(clippy::wildcard_imports)] use crate::ast::*; + ///@@line_break + #[allow(clippy::wildcard_imports)] + use oxc_regular_expression::ast::*; + ///@@line_break #[cfg(target_pointer_width = "64")] const _: () = { #(#assertions_64)* }; diff --git a/tasks/ast_tools/src/main.rs b/tasks/ast_tools/src/main.rs index 5f4e885e4a113..cac3ed40c2ede 100644 --- a/tasks/ast_tools/src/main.rs +++ b/tasks/ast_tools/src/main.rs @@ -34,6 +34,7 @@ static SOURCE_PATHS: &[&str] = &[ "crates/oxc_syntax/src/operator.rs", "crates/oxc_span/src/span/types.rs", "crates/oxc_span/src/source_type/types.rs", + "crates/oxc_regular_expression/src/ast.rs", ]; const AST_CRATE: &str = "crates/oxc_ast";