fix(es/lexer): Don't report lexer errors while backtracking (#3051)

swc_ecma_parser:
 - Share backtracking state with the lexer.
 - Don't report lexing errors while backtracking (Closes #2896)
This commit is contained in:
Donny/강동윤 2021-12-16 19:57:19 +09:00 committed by GitHub
parent c658af4365
commit 61e9b5f841
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 199 additions and 14 deletions

View File

@ -0,0 +1,19 @@
/*#__PURE__*/ React.createElement("div", null, "Dot goes here: \xb7 &notAnEntity; ");
/*#__PURE__*/ React.createElement("div", null, "Be careful of \"-ed strings!");
/*#__PURE__*/ React.createElement("div", null, "{{braces}}");
// Escapes do nothing
/*#__PURE__*/ React.createElement("div", null, "\\n");
// Also works in string literal attributes
/*#__PURE__*/ React.createElement("div", {
attr: "{…}\\"
});
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
/*#__PURE__*/ React.createElement("div", {
attr: "{…}\""
});
// Preserves single quotes
/*#__PURE__*/ React.createElement("div", {
attr: "\""
});
// https://github.com/microsoft/TypeScript/issues/35732
/*#__PURE__*/ React.createElement("div", null, "🐈🐕🐇🐑");

View File

@ -0,0 +1,7 @@
React.createElement("div", null, "Dot goes here: \xb7 &notAnEntity; "), React.createElement("div", null, "Be careful of \"-ed strings!"), React.createElement("div", null, "{{braces}}"), React.createElement("div", null, "\\n"), React.createElement("div", {
attr: "{…}\\"
}), React.createElement("div", {
attr: "{…}\""
}), React.createElement("div", {
attr: "\""
}), React.createElement("div", null, "🐈🐕🐇🐑");

View File

@ -0,0 +1,19 @@
/*#__PURE__*/ React.createElement("div", null, "Dot goes here: \xb7 &notAnEntity; ");
/*#__PURE__*/ React.createElement("div", null, "Be careful of \"-ed strings!");
/*#__PURE__*/ React.createElement("div", null, "{{braces}}");
// Escapes do nothing
/*#__PURE__*/ React.createElement("div", null, "\\n");
// Also works in string literal attributes
/*#__PURE__*/ React.createElement("div", {
attr: "{…}\\"
});
// Does not happen for a string literal that happens to be inside an attribute (and escapes then work)
/*#__PURE__*/ React.createElement("div", {
attr: "{…}\""
});
// Preserves single quotes
/*#__PURE__*/ React.createElement("div", {
attr: "\""
});
// https://github.com/microsoft/TypeScript/issues/35732
/*#__PURE__*/ React.createElement("div", null, "🐈🐕🐇🐑");

View File

@ -0,0 +1,7 @@
React.createElement("div", null, "Dot goes here: \xb7 &notAnEntity; "), React.createElement("div", null, "Be careful of \"-ed strings!"), React.createElement("div", null, "{{braces}}"), React.createElement("div", null, "\\n"), React.createElement("div", {
attr: "{…}\\"
}), React.createElement("div", {
attr: "{…}\""
}), React.createElement("div", {
attr: "\""
}), React.createElement("div", null, "🐈🐕🐇🐑");

View File

@ -16,6 +16,7 @@ use swc_common::{
comments::{Comment, CommentKind}, comments::{Comment, CommentKind},
BytePos, Span, SyntaxContext, BytePos, Span, SyntaxContext,
}; };
use tracing::warn;
use unicode_xid::UnicodeXID; use unicode_xid::UnicodeXID;
/// Collector for raw string. /// Collector for raw string.
@ -117,6 +118,11 @@ impl<'a, I: Input> Lexer<'a, I> {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
pub(super) fn emit_error_span(&mut self, span: Span, kind: SyntaxError) { pub(super) fn emit_error_span(&mut self, span: Span, kind: SyntaxError) {
if self.ctx.ignore_error {
return;
}
warn!("Lexer error at {:?}", span);
let err = Error { let err = Error {
error: Box::new((span, kind)), error: Box::new((span, kind)),
}; };

View File

@ -326,6 +326,9 @@ pub struct EsConfig {
/// Syntactic context. /// Syntactic context.
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct Context { pub struct Context {
/// `true` while backtracking
ignore_error: bool,
/// Is in module code? /// Is in module code?
module: bool, module: bool,
can_be_module: bool, can_be_module: bool,

View File

@ -39,8 +39,6 @@ pub type PResult<T> = Result<T, Error>;
/// EcmaScript parser. /// EcmaScript parser.
#[derive(Clone)] #[derive(Clone)]
pub struct Parser<I: Tokens> { pub struct Parser<I: Tokens> {
/// [false] while backtracking
emit_err: bool,
state: State, state: State,
input: Buffer<I>, input: Buffer<I>,
} }
@ -63,7 +61,6 @@ impl<'a, I: Input> Parser<Lexer<'a, I>> {
impl<I: Tokens> Parser<I> { impl<I: Tokens> Parser<I> {
pub fn new_from(input: I) -> Self { pub fn new_from(input: I) -> Self {
Parser { Parser {
emit_err: true,
state: Default::default(), state: Default::default(),
input: Buffer::new(input), input: Buffer::new(input),
} }
@ -209,7 +206,7 @@ impl<I: Tokens> Parser<I> {
#[cold] #[cold]
fn emit_err(&self, span: Span, error: SyntaxError) { fn emit_err(&self, span: Span, error: SyntaxError) {
if !self.emit_err || !self.syntax().early_errors() { if self.ctx().ignore_error || !self.syntax().early_errors() {
return; return;
} }
@ -220,7 +217,7 @@ impl<I: Tokens> Parser<I> {
#[cold] #[cold]
fn emit_error(&self, error: Error) { fn emit_error(&self, error: Error) {
if !self.emit_err || !self.syntax().early_errors() { if self.ctx().ignore_error || !self.syntax().early_errors() {
return; return;
} }
@ -229,7 +226,7 @@ impl<I: Tokens> Parser<I> {
#[cold] #[cold]
fn emit_strict_mode_err(&self, span: Span, error: SyntaxError) { fn emit_strict_mode_err(&self, span: Span, error: SyntaxError) {
if !self.emit_err { if self.ctx().ignore_error {
return; return;
} }
let error = Error { let error = Error {

View File

@ -490,14 +490,22 @@ impl<I: Tokens> Parser<I> {
if !self.input.syntax().typescript() { if !self.input.syntax().typescript() {
return Ok(false); return Ok(false);
} }
let prev_emit_err = self.emit_err; let prev_ignore_error = self.input.get_ctx().ignore_error;
let mut cloned = self.clone(); let mut cloned = self.clone();
cloned.emit_err = false; let ctx = Context {
ignore_error: true,
..self.input.get_ctx()
};
cloned.set_ctx(ctx);
let res = op(&mut cloned); let res = op(&mut cloned);
match res { match res {
Ok(Some(res)) if res => { Ok(Some(res)) if res => {
*self = cloned; *self = cloned;
self.emit_err = prev_emit_err; let ctx = Context {
ignore_error: prev_ignore_error,
..self.input.get_ctx()
};
self.input.set_ctx(ctx);
Ok(res) Ok(res)
} }
Err(err) => Ok(false), Err(err) => Ok(false),
@ -513,18 +521,32 @@ impl<I: Tokens> Parser<I> {
if !self.input.syntax().typescript() { if !self.input.syntax().typescript() {
return None; return None;
} }
trace_cur!(self, try_parse_ts); #[cfg(feature = "debug")]
let prev_emit_err = self.emit_err; let _tracing = {
let cur = format!("{:?}", self.input.cur());
tracing::span!(tracing::Level::ERROR, "try_parse_ts", cur = &*cur).entered()
};
trace_cur!(self, try_parse_ts);
let prev_ignore_error = self.input.get_ctx().ignore_error;
let mut cloned = self.clone(); let mut cloned = self.clone();
cloned.emit_err = false; let ctx = Context {
ignore_error: true,
..self.input.get_ctx()
};
cloned.set_ctx(ctx);
let res = op(&mut cloned); let res = op(&mut cloned);
match res { match res {
Ok(Some(res)) => { Ok(Some(res)) => {
*self = cloned; *self = cloned;
trace_cur!(self, try_parse_ts__success_value); trace_cur!(self, try_parse_ts__success_value);
let ctx = Context {
ignore_error: prev_ignore_error,
..self.input.get_ctx()
};
self.input.set_ctx(ctx);
self.emit_err = prev_emit_err;
Some(res) Some(res)
} }
Ok(None) => { Ok(None) => {
@ -1069,7 +1091,11 @@ impl<I: Tokens> Parser<I> {
debug_assert!(self.input.syntax().typescript()); debug_assert!(self.input.syntax().typescript());
let mut cloned = self.clone(); let mut cloned = self.clone();
cloned.emit_err = false; let ctx = Context {
ignore_error: true,
..cloned.ctx()
};
cloned.set_ctx(ctx);
let res = op(&mut cloned); let res = op(&mut cloned);
res res
} }

View File

@ -0,0 +1 @@
<div>children</div>; '<>hello</>';

View File

@ -0,0 +1,100 @@
{
"type": "Script",
"span": {
"start": 0,
"end": 34,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 0,
"end": 20,
"ctxt": 0
},
"expression": {
"type": "JSXElement",
"span": {
"start": 0,
"end": 19,
"ctxt": 0
},
"opening": {
"type": "JSXOpeningElement",
"name": {
"type": "Identifier",
"span": {
"start": 1,
"end": 4,
"ctxt": 0
},
"value": "div",
"optional": false
},
"span": {
"start": 0,
"end": 5,
"ctxt": 0
},
"attributes": [],
"selfClosing": false,
"typeArguments": null
},
"children": [
{
"type": "JSXText",
"span": {
"start": 5,
"end": 13,
"ctxt": 0
},
"value": "children",
"raw": "children"
}
],
"closing": {
"type": "JSXClosingElement",
"span": {
"start": 13,
"end": 19,
"ctxt": 0
},
"name": {
"type": "Identifier",
"span": {
"start": 15,
"end": 18,
"ctxt": 0
},
"value": "div",
"optional": false
}
}
}
},
{
"type": "ExpressionStatement",
"span": {
"start": 21,
"end": 34,
"ctxt": 0
},
"expression": {
"type": "StringLiteral",
"span": {
"start": 21,
"end": 33,
"ctxt": 0
},
"value": "<>hello</>",
"hasEscape": false,
"kind": {
"type": "normal",
"containsQuote": true
}
}
}
],
"interpreter": null
}