Skip to content

Commit 94eac1d

Browse files
committed
Top level await (#627)
Implements a top level await for es2017+, and allow it for typescript and ecmascript (ecmascript requires topLevelAwait: true). Closes #626.
1 parent 42373f9 commit 94eac1d

File tree

10 files changed

+69
-6
lines changed

10 files changed

+69
-6
lines changed

ecmascript/parser/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ pub struct Error {
4646

4747
#[derive(Debug, Clone, PartialEq)]
4848
pub enum SyntaxError {
49+
TopLevelAwait,
50+
4951
LegacyDecimal,
5052
LegacyOctal,
5153
InvalidIdentChar,
@@ -226,6 +228,9 @@ impl<'a> From<ErrorToDiag<'a>> for DiagnosticBuilder<'a> {
226228
#[cold]
227229
fn from(e: ErrorToDiag<'a>) -> Self {
228230
let msg: Cow<'static, _> = match e.error {
231+
TopLevelAwait => "top level await requires target to es2017 or higher and \
232+
topLevelAwait:true for ecmascript"
233+
.into(),
229234
LegacyDecimal => "Legacy decimal escape is not permitted in strict mode".into(),
230235
LegacyOctal => "Legacy octal escape is not permitted in strict mode".into(),
231236
InvalidIdentChar => "Invalid character in identifier".into(),

ecmascript/parser/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,18 @@ impl Syntax {
281281
_ => false,
282282
}
283283
}
284+
285+
pub fn top_level_await(self) -> bool {
286+
match self {
287+
Syntax::Es(EsConfig {
288+
top_level_await: true,
289+
..
290+
})
291+
| Syntax::Typescript(..) => true,
292+
293+
_ => false,
294+
}
295+
}
284296
}
285297

286298
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
@@ -378,6 +390,10 @@ pub struct EsConfig {
378390
/// Stage 3.
379391
#[serde(default)]
380392
pub import_meta: bool,
393+
394+
/// Stage 3.
395+
#[serde(default)]
396+
pub top_level_await: bool,
381397
}
382398

383399
/// Syntactic context.

ecmascript/parser/src/parser/expr/ops.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,10 @@ impl<'a, I: Tokens> Parser<'a, I> {
338338
Ok(expr)
339339
}
340340

341-
fn parse_await_expr(&mut self) -> PResult<'a, Box<Expr>> {
341+
pub(crate) fn parse_await_expr(&mut self) -> PResult<'a, Box<Expr>> {
342342
let start = cur_pos!();
343343

344344
assert_and_bump!("await");
345-
debug_assert!(self.ctx().in_async);
346345

347346
if is!('*') {
348347
syntax_error!(SyntaxError::AwaitStar);

ecmascript/parser/src/parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ where
175175
F: for<'a> FnOnce(&'a mut Parser<'a, Lexer<'a, crate::SourceFileInput<'_>>>) -> Result<Ret, ()>,
176176
{
177177
crate::with_test_sess(s, |sess, input| {
178-
let lexer = Lexer::new(sess, syntax, Default::default(), input, None);
178+
let lexer = Lexer::new(sess, syntax, JscTarget::Es2019, input, None);
179179
f(&mut Parser::new_from(sess, lexer))
180180
})
181181
.unwrap_or_else(|output| panic!("test_parser(): failed to parse \n{}\n{}", s, output))

ecmascript/parser/src/parser/stmt.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ impl<'a, I: Tokens> Parser<'a, I> {
9292
top_level: bool,
9393
decorators: Vec<Decorator>,
9494
) -> PResult<'a, Stmt> {
95+
let start = cur_pos!();
96+
if top_level && is!("await") {
97+
let valid = self.target() >= JscTarget::Es2017 && self.syntax().top_level_await();
98+
99+
if !valid {
100+
self.emit_err(self.input.cur_span(), SyntaxError::TopLevelAwait);
101+
}
102+
103+
let expr = self.parse_await_expr()?;
104+
105+
let span = span!(start);
106+
return Ok(Stmt::Expr(ExprStmt { span, expr }));
107+
}
108+
95109
if self.input.syntax().typescript() && is!("const") && peeked_is!("enum") {
96110
assert_and_bump!("const");
97111
assert_and_bump!("enum");
@@ -1634,4 +1648,20 @@ export default function waitUntil(callback, options = {}) {
16341648
},
16351649
);
16361650
}
1651+
1652+
#[test]
1653+
fn top_level_await() {
1654+
test_parser(
1655+
"await foo",
1656+
Syntax::Es(EsConfig {
1657+
top_level_await: true,
1658+
..Default::default()
1659+
}),
1660+
|p| {
1661+
p.parse_module().map_err(|mut e| {
1662+
e.emit();
1663+
})
1664+
},
1665+
);
1666+
}
16371667
}

ecmascript/parser/tests/test262-error-references/fail/1aefe47e20eb91fa.module.js.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
error: Unexpected token Some(Word(await))
1+
error: top level await requires target to es2017 or higher and topLevelAwait:true for ecmascript
2+
--> $DIR/tests/test262-parser/fail/1aefe47e20eb91fa.module.js:1:1
3+
|
4+
1 | await
5+
| ^^^^^
6+
7+
error: Unexpected token None
28
--> $DIR/tests/test262-parser/fail/1aefe47e20eb91fa.module.js:1:1
39
|
410
1 | await
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
await foo
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: top level await requires target to es2017 or higher and topLevelAwait:true for ecmascript
2+
--> $DIR/tests/typescript-errors/custom/top-level-await-jsc-target/input.ts:1:1
3+
|
4+
1 | await foo
5+
| ^^^^^
6+

ecmascript/parser/tests/typescript/class/decorators/input.ts.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
},
2222
"declare": false,
2323
"span": {
24-
"start": 0,
24+
"start": 5,
2525
"end": 196,
2626
"ctxt": 0
2727
},

ecmascript/parser/tests/typescript/decorators/type-arguments/input.ts.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
},
2222
"declare": false,
2323
"span": {
24-
"start": 0,
24+
"start": 21,
2525
"end": 34,
2626
"ctxt": 0
2727
},

0 commit comments

Comments
 (0)