Browse Source

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.
pull/2551/head
raskad 2 years ago
parent
commit
f19467ab1d
  1. 23
      boa_ast/src/statement_list.rs
  2. 15
      boa_parser/src/parser/expression/primary/mod.rs
  3. 3
      boa_parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs
  4. 6
      boa_parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
  5. 3
      boa_parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs
  6. 35
      boa_parser/src/parser/statement/declaration/hoistable/mod.rs
  7. 41
      boa_parser/src/parser/statement/expression/mod.rs
  8. 11
      boa_parser/src/parser/statement/mod.rs

23
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,
}
}

15
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)) => {

3
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<R> TokenParser<R> for AsyncFunctionDeclaration

6
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<R> TokenParser<R> for AsyncGeneratorDeclaration

3
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<R> TokenParser<R> for GeneratorDeclaration

35
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<R: Read, C: CallableDeclaration>(
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<R: Read, C: CallableDeclaration>(
)));
}
// 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<R: Read, C: CallableDeclaration>(
// 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(&params),
&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(&params, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(),
@ -229,5 +238,25 @@ fn parse_callable_declaration<R: Read, C: CallableDeclaration>(
)));
}
if c.parameters_yield_is_early_error() {
// It is a Syntax Error if FormalParameters Contains YieldExpression is true.
if contains(&params, 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(&params, ContainsSymbol::AwaitExpression) {
return Err(Error::lex(LexError::Syntax(
"invalid await usage in generator function parameters".into(),
params_start_position,
)));
}
}
Ok((name, params, body))
}

41
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)) => {

11
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)

Loading…
Cancel
Save