Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/regexp-flags-unicode-escape.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: patch
swc_ecma_parser: patch
---

fix(es/parser): reject unicode escapes in RegExp flags
5 changes: 5 additions & 0 deletions crates/swc_ecma_parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ pub enum SyntaxError {

DuplicatedRegExpFlags(char),
UnknownRegExpFlags,
/// IdentifierPart in RegExp flags must not contain a UnicodeEscapeSequence.
UnicodeEscapeInRegExpFlags,

TS1003,
TS1005,
Expand Down Expand Up @@ -591,6 +593,9 @@ impl SyntaxError {
format!("Duplicated regular expression flag '{flag}'.").into()
}
SyntaxError::UnknownRegExpFlags => "Unknown regular expression flags.".into(),
SyntaxError::UnicodeEscapeInRegExpFlags => {
"Regular expression flags cannot contain unicode escapes.".into()
}

SyntaxError::TS1003 => "Expected an identifier".into(),
SyntaxError::TS1005 => "Expected a semicolon".into(),
Expand Down
23 changes: 14 additions & 9 deletions crates/swc_ecma_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1870,16 +1870,21 @@ impl<'a> Lexer<'a> {
self.bump(1); // '/'

// Spec says "It is a Syntax Error if IdentifierPart contains a Unicode escape
// sequence." TODO: check for escape

// Need to use `read_word` because '\uXXXX' sequences are allowed
// here (don't ask).
// let flags_start = self.cur_pos();
// sequence."
// Need to use `read_word` because '\uXXXX' sequences are accepted by the
// scanner — we still reject them as an early error below.
// Use `cur_as_char` so non-ASCII IdentifierPart flags (e.g. `/a/π\u0067`)
// are scanned; `cur()` only yields the first UTF-8 byte and would miss them.
let flags = {
match self.cur() {
Some(c) if c.is_ident_start() => self
.read_word_as_str_with()
.map(|(s, _)| Some(self.atom(s))),
match self.cur_as_char() {
Some(c) if c == '\\' || c.is_ident_part() => {
let (s, has_escape) = self.read_word_as_str_with()?;
if has_escape {
let span = self.span(start);
self.emit_error_span(span, SyntaxError::UnicodeEscapeInRegExpFlags);
}
Ok(Some(self.atom(s)))
}
_ => Ok(None),
}
}?;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/pattern/g\u0069m;
/pattern/\u0067im;
/pattern/gi\u006d;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/escaped-among-ascii/input.js:1:1]
1 | /pattern/g\u0069m;
: ^^^^^^^^^^^^^^^^^
2 | /pattern/\u0067im;
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/escaped-among-ascii/input.js:2:1]
1 | /pattern/g\u0069m;
2 | /pattern/\u0067im;
: ^^^^^^^^^^^^^^^^^
3 | /pattern/gi\u006d;
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/escaped-among-ascii/input.js:3:1]
2 | /pattern/\u0067im;
3 | /pattern/gi\u006d;
: ^^^^^^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/ok/gimuy;
/bad/g\u0069m;
/also/\u0067;
/leading/\u{79};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/mixed-valid-invalid/input.js:2:1]
1 | /ok/gimuy;
2 | /bad/g\u0069m;
: ^^^^^^^^^^^^^
3 | /also/\u0067;
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/mixed-valid-invalid/input.js:3:1]
2 | /bad/g\u0069m;
3 | /also/\u0067;
: ^^^^^^^^^^^^
4 | /leading/\u{79};
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/mixed-valid-invalid/input.js:4:1]
3 | /also/\u0067;
4 | /leading/\u{79};
: ^^^^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/a/π\u0067;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/non-ascii-start/input.js:1:1]
1 | /a/π\u0067;
: ^^^^^^^^^^
`----
x Unknown regular expression flags.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/non-ascii-start/input.js:1:1]
1 | /a/π\u0067;
: ^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/x/\u0067;
/x/\u{0069};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/only-escaped-flag/input.js:1:1]
1 | /x/\u0067;
: ^^^^^^^^^
2 | /x/\u{0069};
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/only-escaped-flag/input.js:2:1]
1 | /x/\u0067;
2 | /x/\u{0069};
: ^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/a/\u{69};
/foo/g\u{0069};
/\w+/\u{006d}\u{0073};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/u-brace-escape/input.js:1:1]
1 | /a/\u{69};
: ^^^^^^^^^
2 | /foo/g\u{0069};
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/u-brace-escape/input.js:2:1]
1 | /a/\u{69};
2 | /foo/g\u{0069};
: ^^^^^^^^^^^^^^
3 | /\w+/\u{006d}\u{0073};
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/u-brace-escape/input.js:3:1]
2 | /foo/g\u{0069};
3 | /\w+/\u{006d}\u{0073};
: ^^^^^^^^^^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/a/g\u0069;
/\w+/m\u0073;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/u-escape/input.js:1:1]
1 | /a/g\u0069;
: ^^^^^^^^^^
2 | /\w+/m\u0073;
`----
x Regular expression flags cannot contain unicode escapes.
,-[$DIR/tests/errors/regexp-flags-unicode-escape/u-escape/input.js:2:1]
1 | /a/g\u0069;
2 | /\w+/m\u0073;
: ^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,3 @@
1 | var x = /[a-z]/\ux
: ^
`----
x Expected a semicolon
,-[$DIR/tests/test262-parser/fail/6a96389a0cce57e9.js:1:1]
1 | var x = /[a-z]/\ux
: ^^
`----
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,3 @@
1 | var x = /[a-z]/\\ux
: ^
`----
x Expected a semicolon
,-[$DIR/tests/test262-parser/fail/94535dc25ef762ee.js:1:1]
1 | var x = /[a-z]/\\ux
: ^
`----
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,3 @@
1 | var x = /[P QR]/\\u0067
: ^
`----
x Expected a semicolon
,-[$DIR/tests/test262-parser/fail/e7087ec92f0f3c44.js:1:1]
1 | var x = /[P QR]/\\u0067
: ^
`----
Loading