diff --git a/crates/oxc_ast/src/ast/literal.rs b/crates/oxc_ast/src/ast/literal.rs index b797caaee256a..1a2b5d1f40177 100644 --- a/crates/oxc_ast/src/ast/literal.rs +++ b/crates/oxc_ast/src/ast/literal.rs @@ -128,7 +128,7 @@ pub struct BigIntLiteral<'a> { #[generate_derive(CloneIn, ContentEq, GetSpan, GetSpanMut, ESTree)] #[estree( rename = "Literal", - add_fields(value = crate::serialize::EmptyObject), + add_fields(value = crate::serialize::RegExpLiteralValue(self)), add_ts = "value: {} | null", )] pub struct RegExpLiteral<'a> { diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index 22b0d6a8aa95e..5103510a42a54 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -1974,7 +1974,7 @@ impl Serialize for RegExpLiteral<'_> { map.serialize_entry("end", &self.span.end)?; map.serialize_entry("regex", &crate::serialize::RegExpLiteralRegex(self))?; map.serialize_entry("raw", &self.raw)?; - map.serialize_entry("value", &crate::serialize::EmptyObject)?; + map.serialize_entry("value", &crate::serialize::RegExpLiteralValue(self))?; map.end() } } diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index dd09608bdb7c1..34b79456b4204 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -110,35 +110,46 @@ pub fn bigint_literal_bigint<'a>(lit: &'a BigIntLiteral<'a>) -> Cow<'a, str> { lit.raw.strip_suffix('n').unwrap().cow_replace('_', "") } +/// Serializer for `RegExpLiteral`'s `value` field. +/// +/// `null` for invalid RegExp, otherwise empty object. +pub struct RegExpLiteralValue<'a>(pub &'a RegExpLiteral<'a>); + +impl Serialize for RegExpLiteralValue<'_> { + fn serialize(&self, serializer: S) -> Result { + if matches!(&self.0.regex.pattern, RegExpPattern::Invalid(_)) { + // `null` + ().serialize(serializer) + } else { + // `{}` + let map = serializer.serialize_map(None)?; + map.end() + } + } +} + /// Serializer for `RegExpLiteral`'s `regex` field. pub struct RegExpLiteralRegex<'a>(pub &'a RegExpLiteral<'a>); impl Serialize for RegExpLiteralRegex<'_> { fn serialize(&self, serializer: S) -> Result { let mut map = serializer.serialize_map(None)?; - map.serialize_entry("pattern", &self.0.regex.pattern)?; - // If `raw` field is present, flags must be in same order as in source to match Acorn. - // Count number of set bits in `flags` to get number of flags - // (cheaper than searching through `raw` for last `/`). + // If `raw` field is present, flags must be in same order as in source, + // and `pattern` exactly as in source, to match Acorn. let flags = self.0.regex.flags; if let Some(raw) = &self.0.raw { + // Count number of set bits in `flags` to get number of flags + // (cheaper than searching through `raw` for last `/`) let flags_count = flags.bits().count_ones() as usize; let flags_index = raw.len() - flags_count; + map.serialize_entry("pattern", &raw[1..flags_index - 1])?; map.serialize_entry("flags", &raw[flags_index..])?; } else { + map.serialize_entry("pattern", &self.0.regex.pattern)?; map.serialize_entry("flags", &flags)?; } - map.end() - } -} - -/// A placeholder for `RegExpLiteral`'s `value` field. -pub struct EmptyObject; -impl Serialize for EmptyObject { - fn serialize(&self, serializer: S) -> Result { - let map = serializer.serialize_map(None)?; map.end() } } diff --git a/tasks/coverage/src/tools/estree.rs b/tasks/coverage/src/tools/estree.rs index 90f4f31f9f80c..3bfc6e132f28a 100644 --- a/tasks/coverage/src/tools/estree.rs +++ b/tasks/coverage/src/tools/estree.rs @@ -2,7 +2,10 @@ use std::io::Write; use std::path::{Path, PathBuf}; use oxc::{ - allocator::Allocator, ast::utf8_to_utf16::Utf8ToUtf16, parser::Parser, span::SourceType, + allocator::Allocator, + ast::utf8_to_utf16::Utf8ToUtf16, + parser::{ParseOptions, Parser}, + span::SourceType, }; use crate::{ @@ -57,7 +60,8 @@ impl Case for EstreeTest262Case { let is_module = self.base.is_module(); let source_type = SourceType::default().with_module(is_module); let allocator = Allocator::new(); - let ret = Parser::new(&allocator, source_text, source_type).parse(); + let options = ParseOptions { parse_regular_expression: true, ..ParseOptions::default() }; + let ret = Parser::new(&allocator, source_text, source_type).with_options(options).parse(); // Ignore empty AST or parse errors. let mut program = ret.program; if program.is_empty() || ret.panicked || !ret.errors.is_empty() {