From f19467ab1d120293166bdb10045d607565853f99 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Fri, 20 Jan 2023 16:17:57 +0000 Subject: [PATCH] Fix some HoistableDeclaration parsing errors (#2532) This Pull Request hanges the following: - Add early errors for invalid `yield` and `await` usage in function parameters. - Add missing function types to hoistable ordering. - Do not attempt to parse `async` with a following line terminator as an async function. --- boa_ast/src/statement_list.rs | 23 ++++++++--- .../src/parser/expression/primary/mod.rs | 15 ++++++- .../hoistable/async_function_decl/mod.rs | 3 ++ .../hoistable/async_generator_decl/mod.rs | 6 +++ .../hoistable/generator_decl/mod.rs | 3 ++ .../statement/declaration/hoistable/mod.rs | 35 ++++++++++++++-- .../src/parser/statement/expression/mod.rs | 41 ++++++++++++------- boa_parser/src/parser/statement/mod.rs | 11 ++++- 8 files changed, 111 insertions(+), 26 deletions(-) diff --git a/boa_ast/src/statement_list.rs b/boa_ast/src/statement_list.rs index 2202cc8df0..035d3ea81f 100644 --- a/boa_ast/src/statement_list.rs +++ b/boa_ast/src/statement_list.rs @@ -33,12 +33,23 @@ impl StatementListItem { pub const fn hoistable_order(a: &Self, b: &Self) -> Ordering { match (a, b) { ( - Self::Declaration(Declaration::Function(_)), - Self::Declaration(Declaration::Function(_)), - ) => Ordering::Equal, - (_, Self::Declaration(Declaration::Function(_))) => Ordering::Greater, - (Self::Declaration(Declaration::Function(_)), _) => Ordering::Less, - + _, + Self::Declaration( + Declaration::Function(_) + | Declaration::Generator(_) + | Declaration::AsyncFunction(_) + | Declaration::AsyncGenerator(_), + ), + ) => Ordering::Greater, + ( + Self::Declaration( + Declaration::Function(_) + | Declaration::Generator(_) + | Declaration::AsyncFunction(_) + | Declaration::AsyncGenerator(_), + ), + _, + ) => Ordering::Less, (_, _) => Ordering::Equal, } } diff --git a/boa_parser/src/parser/expression/primary/mod.rs b/boa_parser/src/parser/expression/primary/mod.rs index b46f4c35cb..37e5e7da0b 100644 --- a/boa_parser/src/parser/expression/primary/mod.rs +++ b/boa_parser/src/parser/expression/primary/mod.rs @@ -132,14 +132,25 @@ where } TokenKind::Keyword((Keyword::Async, contain_escaped_char)) => { let contain_escaped_char = *contain_escaped_char; + let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? { + 2 + } else { + 1 + }; + let is_line_terminator = cursor + .peek_is_line_terminator(skip_n, interner)? + .unwrap_or(true); + match cursor.peek(1, interner)?.map(Token::kind) { - Some(TokenKind::Keyword((Keyword::Function, _))) if contain_escaped_char => { + Some(TokenKind::Keyword((Keyword::Function, _))) + if !is_line_terminator && contain_escaped_char => + { Err(Error::general( "Keyword must not contain escaped characters", tok_position, )) } - Some(TokenKind::Keyword((Keyword::Function, _))) => { + Some(TokenKind::Keyword((Keyword::Function, _))) if !is_line_terminator => { cursor.advance(interner); match cursor.peek(1, interner)?.map(Token::kind) { Some(TokenKind::Punctuator(Punctuator::Mul)) => { diff --git a/boa_parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs index 087111661f..df909053d7 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs @@ -65,6 +65,9 @@ impl CallableDeclaration for AsyncFunctionDeclaration { fn body_allow_await(&self) -> bool { true } + fn parameters_await_is_early_error(&self) -> bool { + true + } } impl TokenParser for AsyncFunctionDeclaration diff --git a/boa_parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs index a62ed8d959..2cbabd4f7e 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs @@ -75,6 +75,12 @@ impl CallableDeclaration for AsyncGeneratorDeclaration { fn body_allow_await(&self) -> bool { true } + fn parameters_await_is_early_error(&self) -> bool { + true + } + fn parameters_yield_is_early_error(&self) -> bool { + true + } } impl TokenParser for AsyncGeneratorDeclaration diff --git a/boa_parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs index 7413fd0b93..0dcf3747c2 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs @@ -65,6 +65,9 @@ impl CallableDeclaration for GeneratorDeclaration { fn body_allow_await(&self) -> bool { false } + fn parameters_yield_is_early_error(&self) -> bool { + true + } } impl TokenParser for GeneratorDeclaration diff --git a/boa_parser/src/parser/statement/declaration/hoistable/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/mod.rs index 737baa9be6..bd2bb4b4b9 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/mod.rs @@ -135,6 +135,12 @@ trait CallableDeclaration { fn parameters_allow_await(&self) -> bool; fn body_allow_yield(&self) -> bool; fn body_allow_await(&self) -> bool; + fn parameters_yield_is_early_error(&self) -> bool { + false + } + fn parameters_await_is_early_error(&self) -> bool { + false + } } // This is a helper function to not duplicate code in the individual callable declaration parsers. @@ -176,7 +182,7 @@ fn parse_callable_declaration( cursor.expect(Punctuator::CloseBlock, c.error_context(), interner)?; - // Early Error: If the source code matching FormalParameters is strict mode code, + // If the source text matched by FormalParameters is strict mode code, // the Early Error rules for UniqueFormalParameters : FormalParameters are applied. if (cursor.strict_mode() || body.strict()) && params.has_duplicates() { return Err(Error::lex(LexError::Syntax( @@ -185,7 +191,7 @@ fn parse_callable_declaration( ))); } - // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true + // It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true // and IsSimpleParameterList of FormalParameters is false. if body.strict() && !params.is_simple() { return Err(Error::lex(LexError::Syntax( @@ -215,13 +221,16 @@ fn parse_callable_declaration( // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. - // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors name_in_lexically_declared_names( &bound_names(¶ms), &top_level_lexically_declared_names(&body), params_start_position, )?; + // It is a Syntax Error if FormalParameters Contains SuperProperty is true. + // It is a Syntax Error if FunctionBody Contains SuperProperty is true. + // It is a Syntax Error if FormalParameters Contains SuperCall is true. + // It is a Syntax Error if FunctionBody Contains SuperCall is true. if contains(&body, ContainsSymbol::Super) || contains(¶ms, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( "invalid super usage".into(), @@ -229,5 +238,25 @@ fn parse_callable_declaration( ))); } + if c.parameters_yield_is_early_error() { + // It is a Syntax Error if FormalParameters Contains YieldExpression is true. + if contains(¶ms, ContainsSymbol::YieldExpression) { + return Err(Error::lex(LexError::Syntax( + "invalid yield usage in generator function parameters".into(), + params_start_position, + ))); + } + } + + if c.parameters_await_is_early_error() { + // It is a Syntax Error if FormalParameters Contains AwaitExpression is true. + if contains(¶ms, ContainsSymbol::AwaitExpression) { + return Err(Error::lex(LexError::Syntax( + "invalid await usage in generator function parameters".into(), + params_start_position, + ))); + } + } + Ok((name, params, body)) } diff --git a/boa_parser/src/parser/statement/expression/mod.rs b/boa_parser/src/parser/statement/expression/mod.rs index 220efcdca7..758e38e211 100644 --- a/boa_parser/src/parser/statement/expression/mod.rs +++ b/boa_parser/src/parser/statement/expression/mod.rs @@ -63,21 +63,34 @@ where )); } TokenKind::Keyword((Keyword::Async, false)) => { - let next_token = cursor.peek(1, interner).or_abrupt()?; - match next_token.kind() { - TokenKind::Keyword((Keyword::Function, true)) => { - return Err(Error::general( - "Keyword must not contain escaped characters", - next_token.span().start(), - )); - } - TokenKind::Keyword((Keyword::Function, false)) => { - return Err(Error::general( - "expected statement", - next_token.span().start(), - )); + let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? { + 2 + } else { + 1 + }; + let is_line_terminator = cursor + .peek_is_line_terminator(skip_n, interner)? + .unwrap_or(true); + + if is_line_terminator { + {} + } else { + let next_token = cursor.peek(1, interner).or_abrupt()?; + match next_token.kind() { + TokenKind::Keyword((Keyword::Function, true)) => { + return Err(Error::general( + "Keyword must not contain escaped characters", + next_token.span().start(), + )); + } + TokenKind::Keyword((Keyword::Function, false)) => { + return Err(Error::general( + "expected statement", + next_token.span().start(), + )); + } + _ => {} } - _ => {} } } TokenKind::Keyword((Keyword::Let, false)) => { diff --git a/boa_parser/src/parser/statement/mod.rs b/boa_parser/src/parser/statement/mod.rs index 3265da6257..c2452a55cb 100644 --- a/boa_parser/src/parser/statement/mod.rs +++ b/boa_parser/src/parser/statement/mod.rs @@ -374,8 +374,17 @@ where .parse(cursor, interner) .map(ast::StatementListItem::from), TokenKind::Keyword((Keyword::Async, _)) => { + let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? { + 2 + } else { + 1 + }; + let is_line_terminator = cursor + .peek_is_line_terminator(skip_n, interner)? + .unwrap_or(true); + match cursor.peek(1, interner)?.map(Token::kind) { - Some(TokenKind::Keyword((Keyword::Function, _))) => { + Some(TokenKind::Keyword((Keyword::Function, _))) if !is_line_terminator => { Declaration::new(self.allow_yield, self.allow_await) .parse(cursor, interner) .map(ast::StatementListItem::from)