Skip to content

Commit

Permalink
Top level await (#627)
Browse files Browse the repository at this point in the history
Implements a top level await for es2017+, and allow it for typescript and ecmascript (ecmascript requires topLevelAwait: true).

Closes #626.
  • Loading branch information
kdy1 committed Feb 5, 2020
1 parent 42373f9 commit 94eac1d
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 6 deletions.
5 changes: 5 additions & 0 deletions ecmascript/parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct Error {

#[derive(Debug, Clone, PartialEq)]
pub enum SyntaxError {
TopLevelAwait,

LegacyDecimal,
LegacyOctal,
InvalidIdentChar,
Expand Down Expand Up @@ -226,6 +228,9 @@ impl<'a> From<ErrorToDiag<'a>> 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(),
Expand Down
16 changes: 16 additions & 0 deletions ecmascript/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions ecmascript/parser/src/parser/expr/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,10 @@ impl<'a, I: Tokens> Parser<'a, I> {
Ok(expr)
}

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

assert_and_bump!("await");
debug_assert!(self.ctx().in_async);

if is!('*') {
syntax_error!(SyntaxError::AwaitStar);
Expand Down
2 changes: 1 addition & 1 deletion ecmascript/parser/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ where
F: for<'a> FnOnce(&'a mut Parser<'a, Lexer<'a, crate::SourceFileInput<'_>>>) -> Result<Ret, ()>,
{
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))
Expand Down
30 changes: 30 additions & 0 deletions ecmascript/parser/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ impl<'a, I: Tokens> Parser<'a, I> {
top_level: bool,
decorators: Vec<Decorator>,
) -> 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");
Expand Down Expand Up @@ -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();
})
},
);
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
await foo
Original file line number Diff line number Diff line change
@@ -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
| ^^^^^

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"declare": false,
"span": {
"start": 0,
"start": 5,
"end": 196,
"ctxt": 0
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"declare": false,
"span": {
"start": 0,
"start": 21,
"end": 34,
"ctxt": 0
},
Expand Down

0 comments on commit 94eac1d

Please sign in to comment.