mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(regular_expression): reject incomplete Unicode atom escapes (#28448)
Reject a consumed backslash without a valid Unicode AtomEscape, including trailing escapes, and report the invalid escape at its source span. Validated with scoped Rust checks, full conformance, parser bindings, the linter suite. AI-assisted (Codex).
This commit is contained in:
1 parent
fc0eed3404
commit
cb64638ed4
8 files changed
+79
-36
No files matched your search
@@ -145,19 +145,19 @@ source: crates/oxc_linter/src/tester.rs
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
|
||||
⚠ eslint(no-invalid-regexp): Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[no_invalid_regexp.tsx:1:15]
|
||||
⚠ eslint(no-invalid-regexp): Invalid regular expression: Invalid escape sequence
|
||||
╭─[no_invalid_regexp.tsx:1:13]
|
||||
1 │ new RegExp('\\a', 'u');
|
||||
· ▲
|
||||
· ──
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
⚠ eslint(no-invalid-regexp): Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[no_invalid_regexp.tsx:1:15]
|
||||
⚠ eslint(no-invalid-regexp): Invalid regular expression: Invalid escape sequence
|
||||
╭─[no_invalid_regexp.tsx:1:13]
|
||||
1 │ new RegExp('\\a', 'u');
|
||||
· ▲
|
||||
· ──
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
⚠ eslint(no-invalid-regexp): Invalid regular expression: Invalid braced quantifier
|
||||
╭─[no_invalid_regexp.tsx:1:15]
|
||||
|
||||
@@ -75,6 +75,13 @@ pub fn invalid_extended_atom_escape(span: Span) -> OxcDiagnostic {
|
||||
.with_help("Use a valid escape sequence or remove the backslash")
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub fn invalid_atom_escape(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Invalid escape sequence"))
|
||||
.with_label(span)
|
||||
.with_help("Use a valid escape sequence or escape the backslash with `\\\\`")
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub fn invalid_braced_quantifier(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::error(format!("{PREFIX} Invalid braced quantifier"))
|
||||
|
||||
@@ -12,6 +12,40 @@ mod test {
|
||||
|
||||
use crate::{ConstructorParser, LiteralParser, Options, ast::Term};
|
||||
|
||||
#[test]
|
||||
fn trailing_escape() {
|
||||
let allocator = Allocator::default();
|
||||
for flags in ["", "u", "v"] {
|
||||
for pattern in [r"\", r"a\", r"a|\"] {
|
||||
assert!(
|
||||
LiteralParser::new(&allocator, pattern, Some(flags), Options::default())
|
||||
.parse()
|
||||
.is_err(),
|
||||
"/{pattern}/{flags}"
|
||||
);
|
||||
}
|
||||
let flags_source = format!("\"{flags}\"");
|
||||
for source in [r#""\\""#, r#""a\\""#, r"`a\\`"] {
|
||||
assert!(
|
||||
ConstructorParser::new(
|
||||
&allocator,
|
||||
source,
|
||||
Some(&flags_source),
|
||||
Options::default()
|
||||
)
|
||||
.parse()
|
||||
.is_err(),
|
||||
"new RegExp({source}, {flags_source})"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
LiteralParser::new(&allocator, r"\\", Some(flags), Options::default())
|
||||
.parse()
|
||||
.is_ok()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escaped_group_names() {
|
||||
let allocator = Allocator::default();
|
||||
|
||||
@@ -359,10 +359,12 @@ impl<'a> PatternParser<'a> {
|
||||
}
|
||||
|
||||
// \ AtomEscape[?UnicodeMode, ?NamedCaptureGroups]
|
||||
if self.reader.eat('\\')
|
||||
&& let Some(atom_escape) = self.parse_atom_escape(span_start)?
|
||||
{
|
||||
return Ok(Some(atom_escape));
|
||||
if self.reader.eat('\\') {
|
||||
return self.parse_atom_escape(span_start)?.map(Some).ok_or_else(|| {
|
||||
diagnostics::invalid_atom_escape(
|
||||
self.span_factory.create(span_start, self.reader.offset()),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
// CharacterClass[?UnicodeMode, ?UnicodeSetsMode]
|
||||
|
||||
@@ -283,12 +283,12 @@ expression: snapshot
|
||||
|
||||
# invalid_extended_atom_escape
|
||||
|
||||
× Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[/\c0/u:1:3]
|
||||
× Invalid regular expression: Invalid escape sequence
|
||||
╭─[/\c0/u:1:2]
|
||||
1 │ /\c0/u
|
||||
· ▲
|
||||
· ─
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
# duplicated_modifiers
|
||||
|
||||
|
||||
@@ -5144,12 +5144,12 @@ Negative Passed: 265/265 (100.00%)
|
||||
╰────
|
||||
help: This character or escape sequence is not valid inside a character class
|
||||
|
||||
× Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[misc/fail/regexp-unicode-leading-zero-reference.js:1:5]
|
||||
× Invalid regular expression: Invalid escape sequence
|
||||
╭─[misc/fail/regexp-unicode-leading-zero-reference.js:1:4]
|
||||
1 │ /()\01/u;
|
||||
· ▲
|
||||
· ─
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
× Invalid rest element target in destructuring assignment
|
||||
╭─[misc/fail/rest-assignment-target.js:1:6]
|
||||
|
||||
@@ -24615,13 +24615,13 @@ Negative Passed: 4653/4653 (100.00%)
|
||||
╰────
|
||||
help: Add the missing closing delimiter
|
||||
|
||||
× Invalid regular expression: Unterminated capturing group
|
||||
╭─[test262/test/language/literals/regexp/named-groups/invalid-identity-escape-in-capture-u.js:16:2]
|
||||
× Invalid regular expression: Invalid escape sequence
|
||||
╭─[test262/test/language/literals/regexp/named-groups/invalid-identity-escape-in-capture-u.js:16:7]
|
||||
15 │
|
||||
16 │ /(?<a>\a)/u;
|
||||
· ──────
|
||||
· ─
|
||||
╰────
|
||||
help: Add the missing closing delimiter
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
× Invalid regular expression: Unterminated capturing group name
|
||||
╭─[test262/test/language/literals/regexp/named-groups/invalid-incomplete-groupname-2-u.js:16:11]
|
||||
@@ -24991,13 +24991,13 @@ Negative Passed: 4653/4653 (100.00%)
|
||||
34 │ ╰─▶ */
|
||||
╰────
|
||||
|
||||
× Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[test262/test/language/literals/regexp/u-invalid-class-escape.js:24:3]
|
||||
× Invalid regular expression: Invalid escape sequence
|
||||
╭─[test262/test/language/literals/regexp/u-invalid-class-escape.js:24:2]
|
||||
23 │
|
||||
24 │ /\c0/u;
|
||||
· ▲
|
||||
· ─
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
× Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[test262/test/language/literals/regexp/u-invalid-extended-pattern-char.js:21:2]
|
||||
@@ -25007,13 +25007,13 @@ Negative Passed: 4653/4653 (100.00%)
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
|
||||
× Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[test262/test/language/literals/regexp/u-invalid-identity-escape.js:23:3]
|
||||
× Invalid regular expression: Invalid escape sequence
|
||||
╭─[test262/test/language/literals/regexp/u-invalid-identity-escape.js:23:2]
|
||||
22 │
|
||||
23 │ /\M/u;
|
||||
· ▲
|
||||
· ─
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
× Invalid regular expression: Invalid indexed reference
|
||||
╭─[test262/test/language/literals/regexp/u-invalid-legacy-octal-escape.js:26:2]
|
||||
|
||||
@@ -12389,14 +12389,14 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
╰────
|
||||
help: Duplicate names must be in different alternatives
|
||||
|
||||
× Invalid regular expression: Could not parse the entire pattern
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/regularExpressionAnnexB.ts:7:5]
|
||||
× Invalid regular expression: Invalid escape sequence
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/regularExpressionAnnexB.ts:7:4]
|
||||
6 │ // Compare to
|
||||
7 │ /\q\u\i\c\k\_\f\o\x\-\j\u\m\p\s/u,
|
||||
· ▲
|
||||
· ─
|
||||
8 │ /[\q\u\i\c\k\_\f\o\x\-\j\u\m\p\s]/u,
|
||||
╰────
|
||||
help: Check for unbalanced brackets or invalid escape sequences
|
||||
help: Use a valid escape sequence or escape the backslash with `\\`
|
||||
|
||||
× Invalid regular expression: Invalid class atom
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/regularExpressionAnnexB.ts:8:5]
|
||||
|
||||
Reference in new issue
Block a user