Browse Source

Handle early errors for declarations in StatementList (#1175)

* Merge {Let,Const,Var}DeclList

* Rustfmt

* Handle early errors for declarations in StatementList
pull/1265/head
0x7D2B 3 years ago committed by GitHub
parent
commit
6723252c92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      boa/src/syntax/ast/node/declaration/mod.rs
  2. 30
      boa/src/syntax/ast/node/statement_list/mod.rs
  3. 21
      boa/src/syntax/parser/expression/assignment/arrow_function.rs
  4. 20
      boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs
  5. 20
      boa/src/syntax/parser/expression/primary/function_expression/mod.rs
  6. 22
      boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs
  7. 22
      boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs
  8. 50
      boa/src/syntax/parser/statement/mod.rs

1
boa/src/syntax/ast/node/declaration/mod.rs

@ -107,6 +107,7 @@ impl Executable for DeclarationList {
}
continue;
}
match &self {
Const(_) => context.create_immutable_binding(
decl.name().to_owned(),

30
boa/src/syntax/ast/node/statement_list/mod.rs

@ -6,7 +6,7 @@ use crate::{
syntax::ast::node::Node,
BoaProfiler, Context, Result, Value,
};
use std::{fmt, ops::Deref, rc::Rc};
use std::{collections::HashSet, fmt, ops::Deref, rc::Rc};
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
@ -55,6 +55,34 @@ impl StatementList {
}
Ok(())
}
pub fn lexically_declared_names(&self) -> HashSet<&str> {
let mut set = HashSet::new();
for stmt in self.items() {
if let Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) = stmt {
for decl in decl_list.as_ref() {
if !set.insert(decl.name()) {
// It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries.
// https://tc39.es/ecma262/#sec-block-static-semantics-early-errors
unreachable!("Redeclaration of {}", decl.name());
}
}
}
}
set
}
pub fn var_declared_names(&self) -> HashSet<&str> {
let mut set = HashSet::new();
for stmt in self.items() {
if let Node::VarDeclList(decl_list) = stmt {
for decl in decl_list.as_ref() {
set.insert(decl.name());
}
}
}
set
}
}
impl Executable for StatementList {

21
boa/src/syntax/parser/expression/assignment/arrow_function.rs

@ -8,13 +8,13 @@
//! [spec]: https://tc39.es/ecma262/#sec-arrow-function-definitions
use super::AssignmentExpression;
use crate::syntax::lexer::TokenKind;
use crate::{
syntax::{
ast::{
node::{ArrowFunctionDecl, FormalParameter, Node, Return, StatementList},
Punctuator,
},
lexer::{Error as LexError, Position, TokenKind},
parser::{
error::{ErrorContext, ParseError, ParseResult},
function::{FormalParameters, FunctionBody},
@ -90,6 +90,25 @@ where
cursor.expect(TokenKind::Punctuator(Punctuator::Arrow), "arrow function")?;
let body = ConciseBody::new(self.allow_in).parse(cursor)?;
// It is a Syntax Error if any element of the BoundNames of ArrowParameters
// also occurs in the LexicallyDeclaredNames of ConciseBody.
// https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
Ok(ArrowFunctionDecl::new(params, body))
}
}

20
boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs

@ -4,7 +4,7 @@ mod tests;
use crate::{
syntax::{
ast::{node::AsyncFunctionExpr, Keyword, Punctuator},
lexer::TokenKind,
lexer::{Error as LexError, Position, TokenKind},
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
@ -74,6 +74,24 @@ where
cursor.expect(Punctuator::CloseBlock, "async function expression")?;
// 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
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
Ok(AsyncFunctionExpr::new(name, params, body))
}
}

20
boa/src/syntax/parser/expression/primary/function_expression/mod.rs

@ -13,7 +13,7 @@ mod tests;
use crate::{
syntax::{
ast::{node::FunctionExpr, Keyword, Punctuator},
lexer::TokenKind,
lexer::{Error as LexError, Position, TokenKind},
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
@ -69,6 +69,24 @@ where
cursor.expect(Punctuator::CloseBlock, "function expression")?;
// 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
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
Ok(FunctionExpr::new(name, params, body))
}
}

22
boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs

@ -5,7 +5,9 @@ use crate::syntax::{
ast::{node::AsyncFunctionDecl, Keyword, Punctuator},
lexer::TokenKind,
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
function::FormalParameters,
function::FunctionBody,
statement::{BindingIdentifier, LexError, Position},
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
@ -84,6 +86,24 @@ where
cursor.expect(Punctuator::CloseBlock, "async function 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
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
Ok(AsyncFunctionDecl::new(name, params, body))
}
}

22
boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs

@ -4,7 +4,9 @@ mod tests;
use crate::syntax::{
ast::{node::FunctionDecl, Keyword, Punctuator},
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
function::FormalParameters,
function::FunctionBody,
statement::{BindingIdentifier, LexError, Position},
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
@ -65,6 +67,24 @@ where
cursor.expect(Punctuator::CloseBlock, "function 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
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
Ok(FunctionDecl::new(name, params, body))
}
}

50
boa/src/syntax/parser/statement/mod.rs

@ -41,13 +41,14 @@ use super::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser
use crate::{
syntax::{
ast::{node, Keyword, Node, Punctuator},
lexer::{Error as LexError, InputElement, TokenKind},
lexer::{Error as LexError, InputElement, Position, TokenKind},
parser::expression::await_expr::AwaitExpression,
},
BoaProfiler,
};
use labelled_stm::LabelledStatement;
use std::collections::HashSet;
use std::io::Read;
/// Statement parsing.
@ -289,6 +290,53 @@ where
while cursor.next_if(Punctuator::Semicolon)?.is_some() {}
}
// Handle any redeclarations
// https://tc39.es/ecma262/#sec-block-static-semantics-early-errors
{
let mut lexically_declared_names: HashSet<&str> = HashSet::new();
let mut var_declared_names: HashSet<&str> = HashSet::new();
// TODO: Use more helpful positions in errors when spans are added to Nodes
for item in &items {
match item {
Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) => {
for decl in decl_list.as_ref() {
// if name in VarDeclaredNames or can't be added to
// LexicallyDeclaredNames, raise an error
if var_declared_names.contains(decl.name())
|| !lexically_declared_names.insert(decl.name())
{
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of variable `{}`", decl.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
Node::VarDeclList(decl_list) => {
for decl in decl_list.as_ref() {
// if name in LexicallyDeclaredNames, raise an error
if lexically_declared_names.contains(decl.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of variable `{}`", decl.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
// otherwise, add to VarDeclaredNames
var_declared_names.insert(decl.name());
}
}
_ => (),
}
}
}
items.sort_by(Node::hoistable_order);
Ok(items.into())

Loading…
Cancel
Save