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 { pub const fn hoistable_order(a: &Self, b: &Self) -> Ordering {
match (a, b) { match (a, b) {
( (
Self::Declaration(Declaration::Function(_)), _,
Self::Declaration(Declaration::Function(_)), Self::Declaration(
) => Ordering::Equal, Declaration::Function(_)
(_, Self::Declaration(Declaration::Function(_))) => Ordering::Greater, | Declaration::Generator(_)
(Self::Declaration(Declaration::Function(_)), _) => Ordering::Less, | Declaration::AsyncFunction(_)
| Declaration::AsyncGenerator(_),
),
) => Ordering::Greater,
(
Self::Declaration(
Declaration::Function(_)
| Declaration::Generator(_)
| Declaration::AsyncFunction(_)
| Declaration::AsyncGenerator(_),
),
_,
) => Ordering::Less,
(_, _) => Ordering::Equal, (_, _) => Ordering::Equal,
} }
} }

15
boa_parser/src/parser/expression/primary/mod.rs

@ -132,14 +132,25 @@ where
} }
TokenKind::Keyword((Keyword::Async, contain_escaped_char)) => { TokenKind::Keyword((Keyword::Async, contain_escaped_char)) => {
let contain_escaped_char = *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) { 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( Err(Error::general(
"Keyword must not contain escaped characters", "Keyword must not contain escaped characters",
tok_position, tok_position,
)) ))
} }
Some(TokenKind::Keyword((Keyword::Function, _))) => { Some(TokenKind::Keyword((Keyword::Function, _))) if !is_line_terminator => {
cursor.advance(interner); cursor.advance(interner);
match cursor.peek(1, interner)?.map(Token::kind) { match cursor.peek(1, interner)?.map(Token::kind) {
Some(TokenKind::Punctuator(Punctuator::Mul)) => { 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 { fn body_allow_await(&self) -> bool {
true true
} }
fn parameters_await_is_early_error(&self) -> bool {
true
}
} }
impl<R> TokenParser<R> for AsyncFunctionDeclaration 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 { fn body_allow_await(&self) -> bool {
true 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 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 { fn body_allow_await(&self) -> bool {
false false
} }
fn parameters_yield_is_early_error(&self) -> bool {
true
}
} }
impl<R> TokenParser<R> for GeneratorDeclaration 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 parameters_allow_await(&self) -> bool;
fn body_allow_yield(&self) -> bool; fn body_allow_yield(&self) -> bool;
fn body_allow_await(&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. // 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)?; 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. // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.
if (cursor.strict_mode() || body.strict()) && params.has_duplicates() { if (cursor.strict_mode() || body.strict()) && params.has_duplicates() {
return Err(Error::lex(LexError::Syntax( 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. // and IsSimpleParameterList of FormalParameters is false.
if body.strict() && !params.is_simple() { if body.strict() && !params.is_simple() {
return Err(Error::lex(LexError::Syntax( 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 // It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody. // also occurs in the LexicallyDeclaredNames of FunctionBody.
// https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
name_in_lexically_declared_names( name_in_lexically_declared_names(
&bound_names(&params), &bound_names(&params),
&top_level_lexically_declared_names(&body), &top_level_lexically_declared_names(&body),
params_start_position, 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) { if contains(&body, ContainsSymbol::Super) || contains(&params, ContainsSymbol::Super) {
return Err(Error::lex(LexError::Syntax( return Err(Error::lex(LexError::Syntax(
"invalid super usage".into(), "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)) Ok((name, params, body))
} }

41
boa_parser/src/parser/statement/expression/mod.rs

@ -63,21 +63,34 @@ where
)); ));
} }
TokenKind::Keyword((Keyword::Async, false)) => { TokenKind::Keyword((Keyword::Async, false)) => {
let next_token = cursor.peek(1, interner).or_abrupt()?; let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {
match next_token.kind() { 2
TokenKind::Keyword((Keyword::Function, true)) => { } else {
return Err(Error::general( 1
"Keyword must not contain escaped characters", };
next_token.span().start(), let is_line_terminator = cursor
)); .peek_is_line_terminator(skip_n, interner)?
} .unwrap_or(true);
TokenKind::Keyword((Keyword::Function, false)) => {
return Err(Error::general( if is_line_terminator {
"expected statement", {}
next_token.span().start(), } 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)) => { TokenKind::Keyword((Keyword::Let, false)) => {

11
boa_parser/src/parser/statement/mod.rs

@ -374,8 +374,17 @@ where
.parse(cursor, interner) .parse(cursor, interner)
.map(ast::StatementListItem::from), .map(ast::StatementListItem::from),
TokenKind::Keyword((Keyword::Async, _)) => { 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) { 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) Declaration::new(self.allow_yield, self.allow_await)
.parse(cursor, interner) .parse(cursor, interner)
.map(ast::StatementListItem::from) .map(ast::StatementListItem::from)

Loading…
Cancel
Save