fix(parser): change unterminated regex error to be non-recoverable (#5285)

closes #5257
This commit is contained in:
Boshen 2024-08-28 01:57:30 +00:00
parent cffce11620
commit e6fd52e2a6
6 changed files with 19 additions and 191 deletions

View file

@ -223,10 +223,10 @@ impl<'a> ParserImpl<'a> {
}
/// Tell lexer to read a regex
pub(crate) fn read_regex(&mut self) -> (u32, RegExpFlags) {
let (token, pattern_end, flags) = self.lexer.next_regex(self.cur_kind());
pub(crate) fn read_regex(&mut self) -> Result<(u32, RegExpFlags)> {
let (token, pattern_end, flags) = self.lexer.next_regex(self.cur_kind())?;
self.token = token;
(pattern_end, flags)
Ok((pattern_end, flags))
}
/// Tell lexer to read a template substitution tail

View file

@ -189,10 +189,9 @@ impl<'a> ParserImpl<'a> {
}
}
Kind::LParen => self.parse_parenthesized_expression(span),
Kind::Slash | Kind::SlashEq => {
let literal = self.parse_literal_regexp();
Ok(self.ast.expression_from_reg_exp_literal(literal))
}
Kind::Slash | Kind::SlashEq => self
.parse_literal_regexp()
.map(|literal| self.ast.expression_from_reg_exp_literal(literal)),
// JSXElement, JSXFragment
Kind::LAngle if self.source_type.is_jsx() => self.parse_jsx_expression(),
_ => self.parse_identifier_expression(),
@ -336,11 +335,11 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.big_int_literal(self.end_span(span), raw, base))
}
pub(crate) fn parse_literal_regexp(&mut self) -> RegExpLiteral<'a> {
pub(crate) fn parse_literal_regexp(&mut self) -> Result<RegExpLiteral<'a>> {
let span = self.start_span();
// split out pattern
let (pattern_end, flags) = self.read_regex();
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];
self.bump_any();
@ -350,11 +349,11 @@ impl<'a> ParserImpl<'a> {
.parse_regular_expression
.then(|| self.parse_regex_pattern(pattern_start, pattern, flags));
self.ast.reg_exp_literal(
Ok(self.ast.reg_exp_literal(
self.end_span(span),
EmptyObject,
RegExp { pattern: self.ast.atom(pattern), flags },
)
))
}
fn parse_regex_pattern(

View file

@ -2,6 +2,7 @@ use oxc_syntax::identifier::is_line_terminator;
use super::{Kind, Lexer, RegExpFlags, Token};
use crate::diagnostics;
use oxc_diagnostics::Result;
impl<'a> Lexer<'a> {
/// Re-tokenize the current `/` or `/=` and return `RegExp`
@ -10,34 +11,31 @@ impl<'a> Lexer<'a> {
/// where a `RegularExpressionLiteral` is permitted
/// Which means the parser needs to re-tokenize on `PrimaryExpression`,
/// `RegularExpressionLiteral` only appear on the right hand side of `PrimaryExpression`
pub(crate) fn next_regex(&mut self, kind: Kind) -> (Token, u32, RegExpFlags) {
pub(crate) fn next_regex(&mut self, kind: Kind) -> Result<(Token, u32, RegExpFlags)> {
self.token.start = self.offset()
- match kind {
Kind::Slash => 1,
Kind::SlashEq => 2,
_ => unreachable!(),
};
let (pattern_end, flags) = self.read_regex();
let (pattern_end, flags) = self.read_regex()?;
self.lookahead.clear();
let token = self.finish_next(Kind::RegExp);
(token, pattern_end, flags)
Ok((token, pattern_end, flags))
}
/// 12.9.5 Regular Expression Literals
fn read_regex(&mut self) -> (u32, RegExpFlags) {
fn read_regex(&mut self) -> Result<(u32, RegExpFlags)> {
let mut in_escape = false;
let mut in_character_class = false;
loop {
match self.next_char() {
None => {
self.error(diagnostics::unterminated_reg_exp(self.unterminated_range()));
return (self.offset(), RegExpFlags::empty());
return Err(diagnostics::unterminated_reg_exp(self.unterminated_range()));
// return (self.offset(), RegExpFlags::empty());
}
Some(c) if is_line_terminator(c) => {
self.error(diagnostics::unterminated_reg_exp(self.unterminated_range()));
#[allow(clippy::cast_possible_truncation)]
let pattern_end = self.offset() - c.len_utf8() as u32;
return (pattern_end, RegExpFlags::empty());
return Err(diagnostics::unterminated_reg_exp(self.unterminated_range()));
}
Some(c) => {
if in_escape {
@ -79,6 +77,6 @@ impl<'a> Lexer<'a> {
flags |= flag;
}
(pattern_end, flags)
Ok((pattern_end, flags))
}
}

View file

@ -1308,12 +1308,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/380/input.js:1:9]
1 │ var x = /
· ──
2 │ /
╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/380/input.js:2:2]
1 │ var x = /
2 │ /
╰────
@ -1696,19 +1690,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/441/input.js:1:1]
1 │ /a\
· ────
2 │ /
╰────
× Invalid regular expression: Invalid escape
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/441/input.js:1:3]
1 │ /a\
· ─
2 │ /
╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/441/input.js:2:2]
1 │ /a\
2 │ /
╰────
@ -8218,11 +8199,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
2 │ /
╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0040/input.js:3:1]
2 │ /
╰────
× Invalid Unicode escape sequence
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0041/input.js:1:17]
1 │ var x = /[a-z]/\ux
@ -8397,11 +8373,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
2 │ /
╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0062/input.js:3:1]
2 │ /
╰────
× Unterminated string
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0063/input.js:1:9]
1 │ var x = "
@ -8942,18 +8913,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
2 │ /
╰────
× Invalid regular expression: Invalid escape
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0157/input.js:1:3]
1 │ /a\
· ─
2 │ /
╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0157/input.js:3:1]
2 │ /
╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0158/input.js:3:1]
2 │

View file

@ -19559,11 +19559,6 @@ Negative Passed: 4220/4220 (100.00%)
· ─
╰────
× Unexpected token
╭─[test262/test/language/line-terminators/invalid-regexp-cr.js:19:1]
18 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/line-terminators/invalid-regexp-lf.js:17:1]
16 │
@ -19572,11 +19567,6 @@ Negative Passed: 4220/4220 (100.00%)
18 │ /
╰────
× Unexpected token
╭─[test262/test/language/line-terminators/invalid-regexp-lf.js:19:1]
18 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/line-terminators/invalid-regexp-ls.js:17:1]
16 │
@ -19584,11 +19574,6 @@ Negative Passed: 4220/4220 (100.00%)
· ──
╰────
× Unexpected token
╭─[test262/test/language/line-terminators/invalid-regexp-ls.js:18:1]
17 │ //
╰────
× Unterminated regular expression
╭─[test262/test/language/line-terminators/invalid-regexp-ps.js:17:1]
16 │
@ -19596,11 +19581,6 @@ Negative Passed: 4220/4220 (100.00%)
· ──
╰────
× Unexpected token
╭─[test262/test/language/line-terminators/invalid-regexp-ps.js:18:1]
17 │ //
╰────
× Unterminated string
╭─[test262/test/language/line-terminators/invalid-string-cr.js:16:1]
15 │
@ -20388,11 +20368,6 @@ Negative Passed: 4220/4220 (100.00%)
30 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A1.3_T1.js:31:1]
30 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A1.3_T3.js:29:1]
28 │
@ -20401,11 +20376,6 @@ Negative Passed: 4220/4220 (100.00%)
30 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A1.3_T3.js:31:1]
30 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A1.5_T1.js:23:1]
22 │
@ -20414,19 +20384,6 @@ Negative Passed: 4220/4220 (100.00%)
24 │ /
╰────
× Invalid regular expression: Invalid escape
╭─[test262/test/language/literals/regexp/S7.8.5_A1.5_T1.js:23:2]
22 │
23 │ /\
· ─
24 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A1.5_T1.js:25:1]
24 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A1.5_T3.js:22:1]
21 │
@ -20435,19 +20392,6 @@ Negative Passed: 4220/4220 (100.00%)
23 │ /
╰────
× Invalid regular expression: Invalid escape
╭─[test262/test/language/literals/regexp/S7.8.5_A1.5_T3.js:22:2]
21 │
22 │ /\
· ─
23 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A1.5_T3.js:24:1]
23 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A2.2_T1.js:23:1]
22 │
@ -20470,11 +20414,6 @@ Negative Passed: 4220/4220 (100.00%)
33 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A2.3_T1.js:34:1]
33 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A2.3_T3.js:33:1]
32 │
@ -20483,11 +20422,6 @@ Negative Passed: 4220/4220 (100.00%)
34 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A2.3_T3.js:35:1]
34 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A2.5_T1.js:28:1]
27 │
@ -20496,19 +20430,6 @@ Negative Passed: 4220/4220 (100.00%)
29 │ /
╰────
× Invalid regular expression: Invalid escape
╭─[test262/test/language/literals/regexp/S7.8.5_A2.5_T1.js:28:3]
27 │
28 │ /a\
· ─
29 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A2.5_T1.js:30:1]
29 │ /
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/S7.8.5_A2.5_T3.js:28:1]
27 │
@ -20517,19 +20438,6 @@ Negative Passed: 4220/4220 (100.00%)
29 │ /
╰────
× Invalid regular expression: Invalid escape
╭─[test262/test/language/literals/regexp/S7.8.5_A2.5_T3.js:28:3]
27 │
28 │ /a\
· ─
29 │ /
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/S7.8.5_A2.5_T3.js:30:1]
29 │ /
╰────
× Unexpected flag G in regular expression literal
╭─[test262/test/language/literals/regexp/early-err-bad-flag.js:18:4]
17 │
@ -20994,11 +20902,6 @@ Negative Passed: 4220/4220 (100.00%)
30 │
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/regexp-first-char-no-line-separator.js:34:1]
33 │ */
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/regexp-first-char-no-paragraph-separator.js:29:1]
28 │
@ -21007,11 +20910,6 @@ Negative Passed: 4220/4220 (100.00%)
30 │
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/regexp-first-char-no-paragraph-separator.js:34:1]
33 │ */
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/regexp-source-char-no-line-separator.js:28:1]
27 │
@ -21020,11 +20918,6 @@ Negative Passed: 4220/4220 (100.00%)
29 │
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/regexp-source-char-no-line-separator.js:33:1]
32 │ */
╰────
× Unterminated regular expression
╭─[test262/test/language/literals/regexp/regexp-source-char-no-paragraph-separator.js:29:1]
28 │
@ -21033,11 +20926,6 @@ Negative Passed: 4220/4220 (100.00%)
30 │
╰────
× Unexpected token
╭─[test262/test/language/literals/regexp/regexp-source-char-no-paragraph-separator.js:34:1]
33 │ */
╰────
× Invalid regular expression: Could not parse the entire pattern
╭─[test262/test/language/literals/regexp/u-invalid-class-escape.js:23:3]
22 │
@ -33624,11 +33512,6 @@ Negative Passed: 4220/4220 (100.00%)
· ───────
╰────
× Expected `}` but found `EOF`
╭─[test262/test/language/statements/function/invalid-function-body-1.js:18:1]
17 │ function __func(){/ ABC}
╰────
× Unexpected token
╭─[test262/test/language/statements/function/invalid-function-body-2.js:17:19]
16 │

View file

@ -20402,17 +20402,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/typeFro
· ────────────
╰────
× Invalid regular expression: Could not parse the entire pattern
╭─[typescript/tests/cases/conformance/parser/ecmascript5/RegularExpressions/parserRegularExpressionDivideAmbiguity4.ts:1:15]
1 │ foo(/notregexp);
· ▲
╰────
× Expected `)` but found `EOF`
╭─[typescript/tests/cases/conformance/parser/ecmascript5/RegularExpressions/parserRegularExpressionDivideAmbiguity4.ts:1:17]
1 │ foo(/notregexp);
╰────
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[typescript/tests/cases/conformance/parser/ecmascript5/RegularExpressions/parserRegularExpressionDivideAmbiguity7.ts:2:3]
1 │ (a/8