From a6391e3806023a3f010ba1ef5c926e40d2694093 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Mon, 18 Mar 2024 03:35:12 +0100 Subject: [PATCH] Fix parsing of `async` in for-of loops --- .../src/parser/expression/assignment/mod.rs | 19 ++++++--- core/parser/src/parser/expression/tests.rs | 26 +++++++++++- .../statement/iteration/for_statement.rs | 42 ++++++++++++------- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/core/parser/src/parser/expression/assignment/mod.rs b/core/parser/src/parser/expression/assignment/mod.rs index 2fd94ff7a9a..3137a193064 100644 --- a/core/parser/src/parser/expression/assignment/mod.rs +++ b/core/parser/src/parser/expression/assignment/mod.rs @@ -141,15 +141,22 @@ where 1 }; + let peek_1 = cursor.peek(1, interner).or_abrupt()?.kind().clone(); if !cursor .peek_is_line_terminator(skip_n, interner) .or_abrupt()? - && matches!( - cursor.peek(1, interner).or_abrupt()?.kind(), - TokenKind::IdentifierName(_) - | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) - | TokenKind::Punctuator(Punctuator::OpenParen) - ) + && (matches!(peek_1, TokenKind::Punctuator(Punctuator::OpenParen)) + || (matches!( + peek_1, + TokenKind::IdentifierName(_) + | TokenKind::Keyword(( + Keyword::Yield | Keyword::Await | Keyword::Of, + _ + )) + ) && matches!( + cursor.peek(2, interner).or_abrupt()?.kind(), + TokenKind::Punctuator(Punctuator::Arrow) + ))) { return Ok( AsyncArrowFunction::new(self.name, self.allow_in, self.allow_yield) diff --git a/core/parser/src/parser/expression/tests.rs b/core/parser/src/parser/expression/tests.rs index a8627d6fbe2..3d3b6ef6c6a 100644 --- a/core/parser/src/parser/expression/tests.rs +++ b/core/parser/src/parser/expression/tests.rs @@ -10,7 +10,8 @@ use boa_ast::{ }, Call, Identifier, Parenthesized, RegExpLiteral, }, - Declaration, Expression, Statement, + function::{AsyncArrowFunction, FormalParameter, FormalParameterList}, + Declaration, Expression, Script, Statement, }; use boa_interner::{Interner, Sym}; use boa_macros::utf16; @@ -685,6 +686,29 @@ fn check_logical_expressions() { check_invalid_script("a || b ?? c"); } +#[test] +fn parse_async_arrow_function_named_of() { + let interner = &mut Interner::default(); + check_script_parser( + "async of => {}", + vec![ + Statement::Expression(Expression::from(AsyncArrowFunction::new( + None, + FormalParameterList::from_parameters(vec![FormalParameter::new( + Variable::from_identifier( + Identifier::new(interner.get_or_intern_static("of", utf16!("of"))), + None, + ), + false, + )]), + Script::default(), + ))) + .into(), + ], + interner, + ); +} + macro_rules! check_non_reserved_identifier { ($keyword:literal) => {{ let interner = &mut Interner::default(); diff --git a/core/parser/src/parser/statement/iteration/for_statement.rs b/core/parser/src/parser/statement/iteration/for_statement.rs index 84488436b8b..6fdf025bfb7 100644 --- a/core/parser/src/parser/statement/iteration/for_statement.rs +++ b/core/parser/src/parser/statement/iteration/for_statement.rs @@ -11,8 +11,9 @@ use crate::{ lexer::{Error as LexError, TokenKind}, parser::{ expression::{AssignmentExpression, Expression}, - statement::declaration::LexicalDeclaration, - statement::{variable::VariableDeclarationList, Statement}, + statement::{ + declaration::LexicalDeclaration, variable::VariableDeclarationList, Statement, + }, AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, }, source::ReadChar, @@ -20,6 +21,7 @@ use crate::{ }; use ast::{ declaration::Binding, + expression::Identifier, operations::{bound_names, var_declared_names}, }; use boa_ast::{ @@ -107,6 +109,7 @@ where } }; + let mut init_is_async_of = false; let init = match cursor.peek(0, interner).or_abrupt()?.kind() { TokenKind::Keyword((Keyword::Var, _)) => { cursor.advance(interner); @@ -136,19 +139,18 @@ where .into(), ), TokenKind::Keyword((Keyword::Async, false)) if !r#await => { - match cursor.peek(1, interner).or_abrupt()?.kind() { - TokenKind::Keyword((Keyword::Of, _)) => { - return Err(Error::lex(LexError::Syntax( - "invalid left-hand side expression 'async' of a for-of loop".into(), - init_position, - ))); - } - _ => Some( - Expression::new(None, false, self.allow_yield, self.allow_await) - .parse(cursor, interner)? - .into(), - ), + if matches!( + cursor.peek(1, interner).or_abrupt()?.kind(), + TokenKind::Keyword((Keyword::Of, false)) + ) { + init_is_async_of = true; } + + Some( + Expression::new(None, false, self.allow_yield, self.allow_await) + .parse(cursor, interner)? + .into(), + ) } TokenKind::Punctuator(Punctuator::Semicolon) => None, _ => Some( @@ -174,6 +176,18 @@ where )); } (Some(init), TokenKind::Keyword((kw @ (Keyword::In | Keyword::Of), false))) => { + if init_is_async_of + && init + == ForLoopInitializer::Expression(ast::Expression::Identifier( + Identifier::new(Sym::ASYNC), + )) + { + return Err(Error::lex(LexError::Syntax( + "invalid left-hand side expression 'async' of a for-of loop".into(), + init_position, + ))); + } + let in_loop = kw == &Keyword::In; let init = initializer_to_iterable_loop_initializer( init,