From ea037ba3fb61af07d0cddfd936f8dd51d8ae99bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 5 Feb 2020 09:46:09 +0900 Subject: [PATCH 1/5] Support top-level await --- ecmascript/parser/src/error.rs | 3 +++ ecmascript/parser/src/parser/expr/ops.rs | 2 +- ecmascript/parser/src/parser/stmt.rs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ecmascript/parser/src/error.rs b/ecmascript/parser/src/error.rs index 998d93e6de33..9047a3c5f6b4 100644 --- a/ecmascript/parser/src/error.rs +++ b/ecmascript/parser/src/error.rs @@ -46,6 +46,8 @@ pub struct Error { #[derive(Debug, Clone, PartialEq)] pub enum SyntaxError { + TopLevelAwait, + LegacyDecimal, LegacyOctal, InvalidIdentChar, @@ -226,6 +228,7 @@ impl<'a> From> for DiagnosticBuilder<'a> { #[cold] fn from(e: ErrorToDiag<'a>) -> Self { let msg: Cow<'static, _> = match e.error { + TopLevelAwait => "top level await requires es2017 or higher".into(), LegacyDecimal => "Legacy decimal escape is not permitted in strict mode".into(), LegacyOctal => "Legacy octal escape is not permitted in strict mode".into(), InvalidIdentChar => "Invalid character in identifier".into(), diff --git a/ecmascript/parser/src/parser/expr/ops.rs b/ecmascript/parser/src/parser/expr/ops.rs index 446582ea1b6d..17def7d36f6f 100644 --- a/ecmascript/parser/src/parser/expr/ops.rs +++ b/ecmascript/parser/src/parser/expr/ops.rs @@ -338,7 +338,7 @@ impl<'a, I: Tokens> Parser<'a, I> { Ok(expr) } - fn parse_await_expr(&mut self) -> PResult<'a, Box> { + pub(super) fn parse_await_expr(&mut self) -> PResult<'a, Box> { let start = cur_pos!(); assert_and_bump!("await"); diff --git a/ecmascript/parser/src/parser/stmt.rs b/ecmascript/parser/src/parser/stmt.rs index d2749a1845e5..affbb10ff4c3 100644 --- a/ecmascript/parser/src/parser/stmt.rs +++ b/ecmascript/parser/src/parser/stmt.rs @@ -92,6 +92,18 @@ impl<'a, I: Tokens> Parser<'a, I> { top_level: bool, decorators: Vec, ) -> PResult<'a, Stmt> { + let start = cur_pos!(); + if top_level && is!("await") { + if self.target() >= JscTarget::Es2017 { + let expr = self.parse_await_expr()?; + + let span = span!(start); + return Ok(Stmt::Expr(ExprStmt { span, expr });) + } + + self.emit_err(span!(start),SyntaxError::TopLevelAwait); + } + if self.input.syntax().typescript() && is!("const") && peeked_is!("enum") { assert_and_bump!("const"); assert_and_bump!("enum"); From 42e8621910fe9533101ad0663e5d8c191a644936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 5 Feb 2020 09:51:45 +0900 Subject: [PATCH 2/5] top_level_await is stage 3 --- ecmascript/parser/src/error.rs | 4 +++- ecmascript/parser/src/lib.rs | 16 ++++++++++++++++ ecmascript/parser/src/parser/expr/ops.rs | 2 +- ecmascript/parser/src/parser/mod.rs | 2 +- ecmascript/parser/src/parser/stmt.rs | 22 +++++++++++++++++++--- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/ecmascript/parser/src/error.rs b/ecmascript/parser/src/error.rs index 9047a3c5f6b4..54165d82c51d 100644 --- a/ecmascript/parser/src/error.rs +++ b/ecmascript/parser/src/error.rs @@ -228,7 +228,9 @@ impl<'a> From> for DiagnosticBuilder<'a> { #[cold] fn from(e: ErrorToDiag<'a>) -> Self { let msg: Cow<'static, _> = match e.error { - TopLevelAwait => "top level await requires es2017 or higher".into(), + TopLevelAwait => "top level await requires target to es2017 or higher and \ + topLevelAwait:true for ecmascript" + .into(), LegacyDecimal => "Legacy decimal escape is not permitted in strict mode".into(), LegacyOctal => "Legacy octal escape is not permitted in strict mode".into(), InvalidIdentChar => "Invalid character in identifier".into(), diff --git a/ecmascript/parser/src/lib.rs b/ecmascript/parser/src/lib.rs index cb5dabb638e9..32e6b6fa6946 100644 --- a/ecmascript/parser/src/lib.rs +++ b/ecmascript/parser/src/lib.rs @@ -281,6 +281,18 @@ impl Syntax { _ => false, } } + + pub fn top_level_await(self) -> bool { + match self { + Syntax::Es(EsConfig { + top_level_await: true, + .. + }) + | Syntax::Typescript(..) => true, + + _ => false, + } + } } #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)] @@ -378,6 +390,10 @@ pub struct EsConfig { /// Stage 3. #[serde(default)] pub import_meta: bool, + + /// Stage 3. + #[serde(default)] + pub top_level_await: bool, } /// Syntactic context. diff --git a/ecmascript/parser/src/parser/expr/ops.rs b/ecmascript/parser/src/parser/expr/ops.rs index 17def7d36f6f..24e31609d647 100644 --- a/ecmascript/parser/src/parser/expr/ops.rs +++ b/ecmascript/parser/src/parser/expr/ops.rs @@ -338,7 +338,7 @@ impl<'a, I: Tokens> Parser<'a, I> { Ok(expr) } - pub(super) fn parse_await_expr(&mut self) -> PResult<'a, Box> { + pub(crate) fn parse_await_expr(&mut self) -> PResult<'a, Box> { let start = cur_pos!(); assert_and_bump!("await"); diff --git a/ecmascript/parser/src/parser/mod.rs b/ecmascript/parser/src/parser/mod.rs index 170f9e4bc3ea..e367a4e74c2b 100644 --- a/ecmascript/parser/src/parser/mod.rs +++ b/ecmascript/parser/src/parser/mod.rs @@ -175,7 +175,7 @@ where F: for<'a> FnOnce(&'a mut Parser<'a, Lexer<'a, crate::SourceFileInput<'_>>>) -> Result, { crate::with_test_sess(s, |sess, input| { - let lexer = Lexer::new(sess, syntax, Default::default(), input, None); + let lexer = Lexer::new(sess, syntax, JscTarget::Es2019, input, None); f(&mut Parser::new_from(sess, lexer)) }) .unwrap_or_else(|output| panic!("test_parser(): failed to parse \n{}\n{}", s, output)) diff --git a/ecmascript/parser/src/parser/stmt.rs b/ecmascript/parser/src/parser/stmt.rs index affbb10ff4c3..7f5dd48d313e 100644 --- a/ecmascript/parser/src/parser/stmt.rs +++ b/ecmascript/parser/src/parser/stmt.rs @@ -94,14 +94,14 @@ impl<'a, I: Tokens> Parser<'a, I> { ) -> PResult<'a, Stmt> { let start = cur_pos!(); if top_level && is!("await") { - if self.target() >= JscTarget::Es2017 { + if self.target() >= JscTarget::Es2017 && self.syntax().top_level_awiat() { let expr = self.parse_await_expr()?; let span = span!(start); - return Ok(Stmt::Expr(ExprStmt { span, expr });) + return Ok(Stmt::Expr(ExprStmt { span, expr })); } - self.emit_err(span!(start),SyntaxError::TopLevelAwait); + self.emit_err(span!(start), SyntaxError::TopLevelAwait); } if self.input.syntax().typescript() && is!("const") && peeked_is!("enum") { @@ -1646,4 +1646,20 @@ export default function waitUntil(callback, options = {}) { }, ); } + + #[test] + fn top_level_await() { + test_parser( + "await foo", + Syntax::Es(EsConfig { + top_level_await: true, + ..Default::default() + }), + |p| { + p.parse_module().map_err(|mut e| { + e.emit(); + }) + }, + ); + } } From cbf195583c6942e2d895fd166eb02ed8fb2bc3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 5 Feb 2020 09:52:49 +0900 Subject: [PATCH 3/5] fixup! top_level_await is stage 3 --- ecmascript/parser/src/parser/stmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecmascript/parser/src/parser/stmt.rs b/ecmascript/parser/src/parser/stmt.rs index 7f5dd48d313e..ff6f607aaae3 100644 --- a/ecmascript/parser/src/parser/stmt.rs +++ b/ecmascript/parser/src/parser/stmt.rs @@ -94,7 +94,7 @@ impl<'a, I: Tokens> Parser<'a, I> { ) -> PResult<'a, Stmt> { let start = cur_pos!(); if top_level && is!("await") { - if self.target() >= JscTarget::Es2017 && self.syntax().top_level_awiat() { + if self.target() >= JscTarget::Es2017 && self.syntax().top_level_await() { let expr = self.parse_await_expr()?; let span = span!(start); From 2f66b4a404eaaaf210d4339abe7f9d57a39d0800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 5 Feb 2020 10:01:01 +0900 Subject: [PATCH 4/5] Add an error reporting test for top level await --- ecmascript/parser/src/parser/expr/ops.rs | 1 - ecmascript/parser/src/parser/stmt.rs | 12 +++++++----- .../custom/top-level-await-jsc-target/input.ts | 1 + .../top-level-await-jsc-target/input.ts.stderr | 6 ++++++ 4 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts create mode 100644 ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts.stderr diff --git a/ecmascript/parser/src/parser/expr/ops.rs b/ecmascript/parser/src/parser/expr/ops.rs index 24e31609d647..bba3609283bf 100644 --- a/ecmascript/parser/src/parser/expr/ops.rs +++ b/ecmascript/parser/src/parser/expr/ops.rs @@ -342,7 +342,6 @@ impl<'a, I: Tokens> Parser<'a, I> { let start = cur_pos!(); assert_and_bump!("await"); - debug_assert!(self.ctx().in_async); if is!('*') { syntax_error!(SyntaxError::AwaitStar); diff --git a/ecmascript/parser/src/parser/stmt.rs b/ecmascript/parser/src/parser/stmt.rs index ff6f607aaae3..dee4a642e074 100644 --- a/ecmascript/parser/src/parser/stmt.rs +++ b/ecmascript/parser/src/parser/stmt.rs @@ -94,14 +94,16 @@ impl<'a, I: Tokens> Parser<'a, I> { ) -> PResult<'a, Stmt> { let start = cur_pos!(); if top_level && is!("await") { - if self.target() >= JscTarget::Es2017 && self.syntax().top_level_await() { - let expr = self.parse_await_expr()?; + let valid = self.target() >= JscTarget::Es2017 && self.syntax().top_level_await(); - let span = span!(start); - return Ok(Stmt::Expr(ExprStmt { span, expr })); + if !valid { + self.emit_err(self.input.cur_span(), SyntaxError::TopLevelAwait); } - self.emit_err(span!(start), SyntaxError::TopLevelAwait); + let expr = self.parse_await_expr()?; + + let span = span!(start); + return Ok(Stmt::Expr(ExprStmt { span, expr })); } if self.input.syntax().typescript() && is!("const") && peeked_is!("enum") { diff --git a/ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts b/ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts new file mode 100644 index 000000000000..3c0716647c0e --- /dev/null +++ b/ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts @@ -0,0 +1 @@ +await foo \ No newline at end of file diff --git a/ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts.stderr b/ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts.stderr new file mode 100644 index 000000000000..b152852acd9b --- /dev/null +++ b/ecmascript/parser/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts.stderr @@ -0,0 +1,6 @@ +error: top level await requires target to es2017 or higher and topLevelAwait:true for ecmascript + --> $DIR/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts:1:1 + | +1 | await foo + | ^^^^^ + From 24af0df8109a7eeea0bef7baa138e38200171ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 5 Feb 2020 10:01:42 +0900 Subject: [PATCH 5/5] Update tests --- .../fail/1aefe47e20eb91fa.module.js.stderr | 8 +++++++- .../tests/typescript/class/decorators/input.ts.json | 2 +- .../typescript/decorators/type-arguments/input.ts.json | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ecmascript/parser/tests/test262-error-references/fail/1aefe47e20eb91fa.module.js.stderr b/ecmascript/parser/tests/test262-error-references/fail/1aefe47e20eb91fa.module.js.stderr index 059ed49f1a03..2f90724be991 100644 --- a/ecmascript/parser/tests/test262-error-references/fail/1aefe47e20eb91fa.module.js.stderr +++ b/ecmascript/parser/tests/test262-error-references/fail/1aefe47e20eb91fa.module.js.stderr @@ -1,4 +1,10 @@ -error: Unexpected token Some(Word(await)) +error: top level await requires target to es2017 or higher and topLevelAwait:true for ecmascript + --> $DIR/tests/test262-parser/fail/1aefe47e20eb91fa.module.js:1:1 + | +1 | await + | ^^^^^ + +error: Unexpected token None --> $DIR/tests/test262-parser/fail/1aefe47e20eb91fa.module.js:1:1 | 1 | await diff --git a/ecmascript/parser/tests/typescript/class/decorators/input.ts.json b/ecmascript/parser/tests/typescript/class/decorators/input.ts.json index 3fa5c109002e..86f5354511df 100644 --- a/ecmascript/parser/tests/typescript/class/decorators/input.ts.json +++ b/ecmascript/parser/tests/typescript/class/decorators/input.ts.json @@ -21,7 +21,7 @@ }, "declare": false, "span": { - "start": 0, + "start": 5, "end": 196, "ctxt": 0 }, diff --git a/ecmascript/parser/tests/typescript/decorators/type-arguments/input.ts.json b/ecmascript/parser/tests/typescript/decorators/type-arguments/input.ts.json index 8537491e72b2..6ccff4987b94 100644 --- a/ecmascript/parser/tests/typescript/decorators/type-arguments/input.ts.json +++ b/ecmascript/parser/tests/typescript/decorators/type-arguments/input.ts.json @@ -21,7 +21,7 @@ }, "declare": false, "span": { - "start": 0, + "start": 21, "end": 34, "ctxt": 0 },