From 94eac1de89a69ce0f2b30ced7d1c3f9fe48640aa Mon Sep 17 00:00:00 2001 From: kdy1 Date: Wed, 5 Feb 2020 01:05:23 +0000 Subject: [PATCH] Top level await (#627) Implements a top level await for es2017+, and allow it for typescript and ecmascript (ecmascript requires topLevelAwait: true). Closes #626. --- ecmascript/parser/src/error.rs | 5 ++++ ecmascript/parser/src/lib.rs | 16 ++++++++++ ecmascript/parser/src/parser/expr/ops.rs | 3 +- ecmascript/parser/src/parser/mod.rs | 2 +- ecmascript/parser/src/parser/stmt.rs | 30 +++++++++++++++++++ .../fail/1aefe47e20eb91fa.module.js.stderr | 8 ++++- .../top-level-await-jsc-target/input.ts | 1 + .../input.ts.stderr | 6 ++++ .../typescript/class/decorators/input.ts.json | 2 +- .../decorators/type-arguments/input.ts.json | 2 +- 10 files changed, 69 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/error.rs b/ecmascript/parser/src/error.rs index 998d93e6de33..54165d82c51d 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,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 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 446582ea1b6d..bba3609283bf 100644 --- a/ecmascript/parser/src/parser/expr/ops.rs +++ b/ecmascript/parser/src/parser/expr/ops.rs @@ -338,11 +338,10 @@ impl<'a, I: Tokens> Parser<'a, I> { Ok(expr) } - 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"); - debug_assert!(self.ctx().in_async); if is!('*') { syntax_error!(SyntaxError::AwaitStar); 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 d2749a1845e5..dee4a642e074 100644 --- a/ecmascript/parser/src/parser/stmt.rs +++ b/ecmascript/parser/src/parser/stmt.rs @@ -92,6 +92,20 @@ impl<'a, I: Tokens> Parser<'a, I> { top_level: bool, decorators: Vec, ) -> PResult<'a, Stmt> { + let start = cur_pos!(); + if top_level && is!("await") { + let valid = self.target() >= JscTarget::Es2017 && self.syntax().top_level_await(); + + if !valid { + self.emit_err(self.input.cur_span(), 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") { assert_and_bump!("const"); assert_and_bump!("enum"); @@ -1634,4 +1648,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(); + }) + }, + ); + } } 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-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 + | ^^^^^ + 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 },