Revision control

Copy as Markdown

Other Tools

use crate::context_stack::{ControlInfo, ControlKind, LabelKind};
use crate::parser_tables_generated::TerminalId;
use crate::DeclarationKind;
use crate::Token;
use crate::{ParseError, Result};
use ast::arena;
use ast::source_atom_set::{CommonSourceAtomSetIndices, SourceAtomSet, SourceAtomSetIndex};
use std::collections::HashMap;
#[derive(Clone, Copy, Debug, PartialEq)]
struct DeclarationInfo {
kind: DeclarationKind,
offset: usize,
}
impl DeclarationInfo {
fn new(kind: DeclarationKind, offset: usize) -> Self {
Self { kind, offset }
}
}
pub type EarlyErrorsResult<'alloc> = Result<'alloc, ()>;
pub trait LexicalEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc>;
}
pub trait VarEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc>;
}
pub trait ParameterEarlyErrorsContext {
fn declare<'alloc>(
&mut self,
name: SourceAtomSetIndex,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc>;
}
pub trait ControlEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc>;
}
// ===========================================================================
// Identifiers
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct IdentifierEarlyErrorsContext {}
impl IdentifierEarlyErrorsContext {
pub fn new() -> Self {
Self {}
}
fn is_strict<'alloc>(&self) -> Result<'alloc, bool> {
Err(ParseError::NotImplemented("strict-mode-only early error is not yet supported").into())
}
// Not used due to NotImplemented before the callsite.
/*
fn is_module(&self) -> Result<bool, ParseError<'alloc>> {
Err(ParseError::NotImplemented(
"module-only early error is not yet supported",
))
}
*/
fn is_arguments_identifier<'alloc>(token: &arena::Box<'alloc, Token>) -> bool {
return (token.terminal_id == TerminalId::Name
|| token.terminal_id == TerminalId::NameWithEscape)
&& token.value.as_atom() == CommonSourceAtomSetIndices::arguments();
}
fn is_eval_identifier<'alloc>(token: &arena::Box<'alloc, Token>) -> bool {
return (token.terminal_id == TerminalId::Name
|| token.terminal_id == TerminalId::NameWithEscape)
&& token.value.as_atom() == CommonSourceAtomSetIndices::eval();
}
fn is_yield_identifier<'alloc>(token: &arena::Box<'alloc, Token>) -> bool {
return token.terminal_id == TerminalId::Yield
|| (token.terminal_id == TerminalId::NameWithEscape
&& token.value.as_atom() == CommonSourceAtomSetIndices::yield_());
}
fn is_await_identifier<'alloc>(token: &arena::Box<'alloc, Token>) -> bool {
return token.terminal_id == TerminalId::Await
|| (token.terminal_id == TerminalId::NameWithEscape
&& token.value.as_atom() == CommonSourceAtomSetIndices::await_());
}
pub fn check_binding_identifier<'alloc>(
&self,
token: &arena::Box<'alloc, Token>,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
if Self::is_arguments_identifier(token) || Self::is_eval_identifier(token) {
// Static Semantics: Early Errors
//
// BindingIdentifier : Identifier
//
// * It is a Syntax Error if the code matched by this
// production is contained in strict mode code and the
// StringValue of Identifier is "arguments" or "eval".
if self.is_strict()? {
let name = atoms.get(token.value.as_atom());
let offset = token.loc.start;
return Err(ParseError::InvalidIdentifier(name.clone(), offset).into());
}
return Ok(());
}
if Self::is_yield_identifier(token) {
// BindingIdentifier : yield
//
// * It is a Syntax Error if this production has a [Yield]
// parameter.
return Err(ParseError::NotImplemented("[Yield] parameter").into());
// return self.check_yield_common();
}
if Self::is_await_identifier(token) {
// BindingIdentifier : await
//
// * It is a Syntax Error if this production has an [Await]
// parameter.
return Err(ParseError::NotImplemented("[Await] parameter").into());
// return self.check_await_common();
}
self.check_identifier(token, atoms)
}
pub fn check_label_identifier<'alloc>(
&self,
token: &arena::Box<'alloc, Token>,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
if Self::is_yield_identifier(token) {
return self.check_yield_common(token, atoms);
}
if Self::is_await_identifier(token) {
return self.check_await_common(token, atoms);
}
self.check_identifier(token, atoms)
}
pub fn check_identifier_reference<'alloc>(
&self,
token: &arena::Box<'alloc, Token>,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
if Self::is_yield_identifier(token) {
return self.check_yield_common(token, atoms);
}
if Self::is_await_identifier(token) {
return self.check_await_common(token, atoms);
}
self.check_identifier(token, atoms)
}
fn check_yield_common<'alloc>(
&self,
_token: &arena::Box<'alloc, Token>,
_atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// IdentifierReference[Yield, Await] : Identifier
//
// BindingIdentifier[Yield, Await] : Identifier
//
// LabelIdentifier[Yield, Await] : Identifier
//
// * It is a Syntax Error if this production has a [Yield] parameter
// and StringValue of Identifier is "yield".
return Err(ParseError::NotImplemented("[Yield] parameter").into());
// IdentifierReference : yield
//
// BindingIdentifier : yield
//
// LabelIdentifier : yield
//
// * It is a Syntax Error if the code matched by this production is
// contained in strict mode code.
//
// and
//
// Identifier : IdentifierName but not ReservedWord
//
// * It is a Syntax Error if this phrase is contained in strict mode
// code and the StringValue of IdentifierName is: "implements",
// "interface", "let", "package", "private", "protected", "public",
// "static", or "yield".
//
// if self.is_strict()? {
// return Err(ParseError::InvalidIdentifier(
// atoms.get(token.value.as_atom()),
// offset,
// ));
// }
//
// Ok(())
}
fn check_await_common<'alloc>(
&self,
_token: &arena::Box<'alloc, Token>,
_atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// IdentifierReference[Yield, Await] : Identifier
//
// BindingIdentifier[Yield, Await] : Identifier
//
// LabelIdentifier[Yield, Await] : Identifier
//
// * It is a Syntax Error if this production has an [Await] parameter
// and StringValue of Identifier is "await".
return Err(ParseError::NotImplemented("[Await] parameter").into());
// IdentifierReference : await
//
// BindingIdentifier : await
//
// LabelIdentifier : await
//
// * It is a Syntax Error if the goal symbol of the syntactic
// grammar is Module.
//
// and
//
// Identifier : IdentifierName but not ReservedWord
//
// * It is a Syntax Error if the goal symbol of the syntactic grammar
// is Module and the StringValue of IdentifierName is "await".
//
// if self.is_module()? {
// return Err(ParseError::InvalidIdentifier(
// atoms.get(token.value.as_atom()),
// offset,
// ));
// }
//
// Ok(())
}
fn is_contextual_keyword_excluding_yield(name: SourceAtomSetIndex) -> bool {
name == CommonSourceAtomSetIndices::implements()
|| name == CommonSourceAtomSetIndices::interface()
|| name == CommonSourceAtomSetIndices::let_()
|| name == CommonSourceAtomSetIndices::package()
|| name == CommonSourceAtomSetIndices::private()
|| name == CommonSourceAtomSetIndices::protected()
|| name == CommonSourceAtomSetIndices::public()
|| name == CommonSourceAtomSetIndices::static_()
}
fn is_keyword(name: SourceAtomSetIndex) -> bool {
name == CommonSourceAtomSetIndices::break_()
|| name == CommonSourceAtomSetIndices::case()
|| name == CommonSourceAtomSetIndices::catch()
|| name == CommonSourceAtomSetIndices::class()
|| name == CommonSourceAtomSetIndices::const_()
|| name == CommonSourceAtomSetIndices::continue_()
|| name == CommonSourceAtomSetIndices::debugger()
|| name == CommonSourceAtomSetIndices::default()
|| name == CommonSourceAtomSetIndices::delete()
|| name == CommonSourceAtomSetIndices::do_()
|| name == CommonSourceAtomSetIndices::else_()
|| name == CommonSourceAtomSetIndices::enum_()
|| name == CommonSourceAtomSetIndices::export()
|| name == CommonSourceAtomSetIndices::extends()
|| name == CommonSourceAtomSetIndices::false_()
|| name == CommonSourceAtomSetIndices::finally()
|| name == CommonSourceAtomSetIndices::for_()
|| name == CommonSourceAtomSetIndices::function()
|| name == CommonSourceAtomSetIndices::if_()
|| name == CommonSourceAtomSetIndices::import()
|| name == CommonSourceAtomSetIndices::in_()
|| name == CommonSourceAtomSetIndices::instanceof()
|| name == CommonSourceAtomSetIndices::new_()
|| name == CommonSourceAtomSetIndices::null()
|| name == CommonSourceAtomSetIndices::return_()
|| name == CommonSourceAtomSetIndices::super_()
|| name == CommonSourceAtomSetIndices::switch()
|| name == CommonSourceAtomSetIndices::this()
|| name == CommonSourceAtomSetIndices::throw()
|| name == CommonSourceAtomSetIndices::true_()
|| name == CommonSourceAtomSetIndices::try_()
|| name == CommonSourceAtomSetIndices::typeof_()
|| name == CommonSourceAtomSetIndices::var()
|| name == CommonSourceAtomSetIndices::void()
|| name == CommonSourceAtomSetIndices::while_()
|| name == CommonSourceAtomSetIndices::with()
}
fn check_identifier<'alloc>(
&self,
token: &arena::Box<'alloc, Token>,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
match token.terminal_id {
TerminalId::NameWithEscape => {
let name = token.value.as_atom();
if Self::is_contextual_keyword_excluding_yield(name) {
// Identifier : IdentifierName but not ReservedWord
//
// * It is a Syntax Error if this phrase is contained
// in strict mode code and the StringValue of
// IdentifierName is:
// "implements", "interface", "let", "package",
// "private", "protected", "public", "static",
// or "yield".
//
// NOTE: "yield" case is handled in
// `check_yield_common`.
if self.is_strict()? {
let name = atoms.get(token.value.as_atom());
let offset = token.loc.start;
return Err(ParseError::InvalidIdentifier(name, offset).into());
}
} else if Self::is_keyword(name) {
// Identifier : IdentifierName but not ReservedWord
//
// * It is a Syntax Error if StringValue of
// IdentifierName is the same String value as the
// StringValue of any ReservedWord except for yield
// or await.
let name = atoms.get(token.value.as_atom());
let offset = token.loc.start;
return Err(ParseError::InvalidIdentifier(name, offset).into());
}
}
TerminalId::Implements
| TerminalId::Interface
| TerminalId::Let
| TerminalId::Package
| TerminalId::Private
| TerminalId::Protected
| TerminalId::Public
| TerminalId::Static => {
// Identifier : IdentifierName but not ReservedWord
//
// * It is a Syntax Error if this phrase is contained in strict
// mode code and the StringValue of IdentifierName is:
// "implements", "interface", "let", "package", "private",
// "protected", "public", "static", or "yield".
//
// NOTE: "yield" case is handled in `check_yield_common`.
if self.is_strict()? {
let name = atoms.get(token.value.as_atom());
let offset = token.loc.start;
return Err(ParseError::InvalidIdentifier(name, offset).into());
}
}
_ => {}
}
Ok(())
}
}
// ===========================================================================
// Block
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct BlockEarlyErrorsContext {
lex_names_of_stmt_list: HashMap<SourceAtomSetIndex, DeclarationInfo>,
var_names_of_stmt_list: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl BlockEarlyErrorsContext {
pub fn new() -> Self {
Self {
lex_names_of_stmt_list: HashMap::new(),
var_names_of_stmt_list: HashMap::new(),
}
}
fn is_supported_lexical(kind: DeclarationKind) -> bool {
match kind {
// LexicallyDeclaredNames of StatementList
//
// Static Semantics: LexicallyDeclaredNames
//
// StatementList => StatementListItem => Declaration
// 1. Return the BoundNames of Declaration.
// Declaration => HoistableDeclaration => FunctionDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return « "*default*" ».
//
// and
//
// StatementList => StatementListItem => Statement
// 1. If Statement is Statement: LabelledStatement, return
// LexicallyDeclaredNames of LabelledStatement.
// 2. Return a new empty List.
// LabelledStatement => LabelledItem => FunctionDeclaration
// 1. Return BoundNames of FunctionDeclaration.
// FunctionDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return « "*default*" ».
//
// NOTE: This is separated from LexicalAsyncOrGenerator to support
DeclarationKind::LexicalFunction |
// StatementList => StatementListItem => Declaration
// 1. Return the BoundNames of Declaration.
// Declaration => HoistableDeclaration => FunctionDeclaration
// Declaration => HoistableDeclaration => GeneratorDeclaration
// Declaration => HoistableDeclaration => AsyncFunctionDeclaration
// Declaration => HoistableDeclaration => AsyncGeneratorDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return « "*default*" ».
DeclarationKind::LexicalAsyncOrGenerator |
// StatementList => StatementListItem => Declaration
// 1. Return the BoundNames of Declaration.
// Declaration => ClassDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return « "*default*" ».
DeclarationKind::Class |
// StatementList => StatementListItem => Declaration
// 1. Return the BoundNames of Declaration.
// Declaration => LexicalDeclaration => BindingList
// => LexicalBinding
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return the BoundNames of BindingPattern.
DeclarationKind::Let |
DeclarationKind::Const => true,
_ => false,
}
}
fn is_supported_var(kind: DeclarationKind) -> bool {
match kind {
// VarDeclaredNames of StatementList
//
// Static Semantics: VarDeclaredNames
//
// StatementList => StatementListItem => Statement
// => VariableStatement
// 1. Return BoundNames of VariableDeclarationList.
// VariableDeclarationList => VariableDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return the BoundNames of BindingPattern.
//
// and
//
// StatementList => StatementListItem => Statement
// => BreakableStatement => IterationStatement => c-style-for-var
// 1. Let names be BoundNames of VariableDeclarationList.
// 2. Append to names the elements of the VarDeclaredNames of
// Statement.
// 3. Return names.
// VariableDeclarationList => VariableDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return the BoundNames of BindingPattern.
//
// and
//
// StatementList => StatementListItem => Statement
// => BreakableStatement => IterationStatement => for-in-var
// 1. Let names be the BoundNames of ForBinding.
// 2. Append to names the elements of the VarDeclaredNames of
// Statement.
// 3. Return names.
// ForBinding => BindingIdentifier
// ForBinding => BindingPattern
DeclarationKind::Var |
// StatementList => StatementListItem => Statement => BlockStatement
// => Block
// StatementList => StatementListItem => Statement => IfStatement
// => Statement
// StatementList => StatementListItem => Statement
// => BreakableStatement => IterationStatement => do-while
// => Statement
// StatementList => StatementListItem => Statement
// => BreakableStatement => IterationStatement => while => Statement
// StatementList => StatementListItem => Statement
// => BreakableStatement => IterationStatement => c-style-for
// => Statement
// StatementList => StatementListItem => Statement
// => BreakableStatement => IterationStatement => for-in
// => Statement
// StatementList => StatementListItem => Statement
// => BreakableStatement => SwitchStatement => CaseBlock
// => CaseClauses => StatementList
// StatementList => StatementListItem => Statement
// => BreakableStatement => SwitchStatement => CaseBlock
// => DefaultClause => StatementList
// StatementList => StatementListItem => Statement => WithStatement
// => Statement
// StatementList => StatementListItem => Statement
// => LabelledStatement => LabelledItem => Statement
// StatementList => StatementListItem => Statement
// => LabelledStatement => LabelledItem => FunctionDeclaration
// 1. Return a new empty List.
// StatementList => StatementListItem => Statement => TryStatement
// => Block
// StatementList => StatementListItem => Statement => TryStatement
// => Catch => Block
// StatementList => StatementListItem => Statement => TryStatement
// => Finally => Block
// StatementList => StatementListItem => Declaration
// 1. Return a new empty List.
// StatementList => StatementListItem => Declaration
// => HoistableDeclaration => FunctionDeclaration
//
// Changes to FunctionDeclarationInstantiation
//
// During FunctionDeclarationInstantiation the following steps are
// performed in place of step 29:
//
// 1. If strict is false, then
// a. For each FunctionDeclaration f that is directly contained
// in the StatementList of a Block, CaseClause, or
// DefaultClause, do
// i. Let F be StringValue of the BindingIdentifier of f.
// ii. If replacing the FunctionDeclaration f with a
// VariableStatement that has F as a BindingIdentifier
// would not produce any Early Errors for func and F is not
// an element of parameterNames, then
//
// and
//
// Changes to GlobalDeclarationInstantiation
//
// During GlobalDeclarationInstantiation the following steps are
// performed in place of step 14:
//
// 1. Let strict be IsStrict of script.
// 2. If strict is false, then
// d. For each FunctionDeclaration f that is directly contained
// in the StatementList of a Block , CaseClause , or
// DefaultClause Contained within script, do
// i. Let F be StringValue of the BindingIdentifier of f.
// ii. If replacing the FunctionDeclaration f with a
// VariableStatement that has F as a BindingIdentifier
// would not produce any Early Errors for script, then
//
// This isn't used while checking actual Early Errors.
DeclarationKind::VarForAnnexBLexicalFunction => true,
_ => false,
}
}
fn is_strict<'alloc>(&self) -> Result<'alloc, bool> {
Err(ParseError::NotImplemented("strict-mode-only early error is not yet supported").into())
}
}
impl LexicalEarlyErrorsContext for BlockEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_lexical(kind));
// Static Semantics: Early Errors
//
// Block : { StatementList }
//
// * It is a Syntax Error if the LexicallyDeclaredNames of StatementList
// contains any duplicate entries.
//
if let Some(info) = self.lex_names_of_stmt_list.get(&name) {
// Changes to Block Static Semantics: Early Errors
//
// Block : { StatementList }
//
// * It is a Syntax Error if the LexicallyDeclaredNames of
// StatementList contains any duplicate entries, ** unless the
// source code matching this production is not strict mode
// code and the duplicate entries are only bound by
// FunctionDeclarations **.
if !(!self.is_strict()?
&& info.kind == DeclarationKind::LexicalFunction
&& kind == DeclarationKind::LexicalFunction)
{
let name = atoms.get(name);
return Err(ParseError::DuplicateBinding(
name,
info.kind,
info.offset,
kind,
offset,
)
.into());
}
}
// Static Semantics: Early Errors
//
// Block : { StatementList }
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of StatementList also occurs in the VarDeclaredNames of
// StatementList.
if let Some(info) = self.var_names_of_stmt_list.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.lex_names_of_stmt_list
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl VarEarlyErrorsContext for BlockEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_var(kind));
// Static Semantics: Early Errors
//
// Block : { StatementList }
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of StatementList also occurs in the VarDeclaredNames of
// StatementList.
if let Some(info) = self.lex_names_of_stmt_list.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.var_names_of_stmt_list
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
// ===========================================================================
// The for Statement
//
// The for-in, for-of, and for-await-of Statements
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct LexicalForHeadEarlyErrorsContext {
bound_names_of_decl: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl LexicalForHeadEarlyErrorsContext {
pub fn new() -> Self {
Self {
bound_names_of_decl: HashMap::new(),
}
}
fn is_supported_lexical(kind: DeclarationKind) -> bool {
match kind {
// BoundNames of BindingList
//
// Static Semantics: BoundNames
//
// BindingList => LexicalBinding
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return the BoundNames of BindingPattern.
//
// and
//
// BoundNames of ForDeclaration
//
// Static Semantics: BoundNames
//
// ForDeclaration => BindingList => LexicalBinding
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return the BoundNames of BindingPattern.
DeclarationKind::Let | DeclarationKind::Const => true,
_ => false,
}
}
}
impl LexicalEarlyErrorsContext for LexicalForHeadEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_lexical(kind));
// Static Semantics: Early Errors
//
// IterationStatement :
// for ( LexicalDeclaration Expression_opt ; Expression_opt )
// Statement
//
// Static Semantics: Early Errors
//
// LexicalDeclaration : LetOrConst BindingList ;
//
// * It is a Syntax Error if the BoundNames of BindingList contains any
// duplicate entries.
//
// and
//
// Static Semantics: Early Errors
//
// IterationStatement :
// for ( ForDeclaration in Expression ) Statement
// for ( ForDeclaration of AssignmentExpression ) Statement
// for await ( ForDeclaration of AssignmentExpression ) Statement
//
// * It is a Syntax Error if the BoundNames of ForDeclaration contains
// any duplicate entries.
if let Some(info) = self.bound_names_of_decl.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.bound_names_of_decl
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
#[derive(Debug, PartialEq)]
struct InternalForBodyEarlyErrorsContext {
var_names_of_stmt: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl InternalForBodyEarlyErrorsContext {
fn new() -> Self {
Self {
var_names_of_stmt: HashMap::new(),
}
}
fn is_supported_var(kind: DeclarationKind) -> bool {
match kind {
// VarDeclaredNames of Statement
//
// See Block::is_supported_var for the details.
DeclarationKind::Var => true,
_ => false,
}
}
}
impl VarEarlyErrorsContext for InternalForBodyEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
_atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_var(kind));
self.var_names_of_stmt
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub struct LexicalForBodyEarlyErrorsContext {
head: LexicalForHeadEarlyErrorsContext,
body: InternalForBodyEarlyErrorsContext,
}
impl LexicalForBodyEarlyErrorsContext {
pub fn new(head: LexicalForHeadEarlyErrorsContext) -> Self {
Self {
head,
body: InternalForBodyEarlyErrorsContext::new(),
}
}
}
impl VarEarlyErrorsContext for LexicalForBodyEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// IterationStatement :
// for ( LexicalDeclaration Expression_opt ; Expression_opt )
// Statement
//
// * It is a Syntax Error if any element of the BoundNames of
// LexicalDeclaration also occurs in the VarDeclaredNames of
// Statement.
//
// and
//
// Static Semantics: Early Errors
//
// IterationStatement :
// for ( ForDeclaration in Expression ) Statement
// for ( ForDeclaration of AssignmentExpression ) Statement
// for await ( ForDeclaration of AssignmentExpression ) Statement
//
// * It is a Syntax Error if any element of the BoundNames of
// ForDeclaration also occurs in the VarDeclaredNames of Statement.
if let Some(info) = self.head.bound_names_of_decl.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.body.declare_var(name, kind, offset, atoms)
}
}
// ===========================================================================
// LabelledStatements
// ===========================================================================
pub struct LabelledStatementEarlyErrorsContext {
name: SourceAtomSetIndex,
kind: LabelKind,
}
impl LabelledStatementEarlyErrorsContext {
pub fn new(name: SourceAtomSetIndex, kind: LabelKind) -> Self {
Self { name, kind }
}
pub fn check_duplicate_label<'alloc>(
&self,
inner_label_name: SourceAtomSetIndex,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: ContainsDuplicateLabels
//
// LabelledStatement : LabelIdentifier : LabelledItem
//
// Static Semantics: Early Errors
//
// * It is a Syntax Error if ContainsDuplicateLabels of StatementList with argument « » is
// true.
//
// and
//
//
// * It is a Syntax Error if ContainsDuplicateLabels of ModuleItemList with argument « » is
// true.
//
// and
//
// * It is a Syntax Error if ContainsDuplicateLabels of FunctionStatementList with argument
// « » is true.
if inner_label_name == self.name {
return Err(ParseError::DuplicateLabel.into());
}
Ok(())
}
pub fn check_labelled_continue_to_non_loop<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc> {
// Continues outside of iterators and Unlabelled breaks can not be detected at the
// function / script level easily, because we only have binding information there, not
// whether the label in question is associated with a loop or not. The nesting errors
// also cannot be detected at the {Break,Continue}Statement level, as we don't have that
// information there either. So we are handling these errors here. This handles labelled
// continues that would otherwise pass.
//
// Static Semantics: Early Errors
//
// ContinueStatement : continue LabelIdentifier ;
//
// * It is a Syntax Error if this ContinueStatement is not nested, directly
// indirectly (but not crossing function boundaries), within an
// IterationStatement.
if let Some(name) = info.label {
if self.kind != LabelKind::Loop
&& info.kind == ControlKind::Continue
&& name == self.name
{
return Err(ParseError::BadContinue.into());
}
}
Ok(())
}
}
// ===========================================================================
// The switch Statement
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct CaseBlockEarlyErrorsContext {
lex_names_of_case_block: HashMap<SourceAtomSetIndex, DeclarationInfo>,
var_names_of_case_block: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl CaseBlockEarlyErrorsContext {
pub fn new() -> Self {
Self {
lex_names_of_case_block: HashMap::new(),
var_names_of_case_block: HashMap::new(),
}
}
fn is_supported_lexical(kind: DeclarationKind) -> bool {
// CaseBlock => CaseClauses => CaseClause => StatementList
// CaseBlock => DefaultClause => StatementList
BlockEarlyErrorsContext::is_supported_lexical(kind)
}
fn is_supported_var(kind: DeclarationKind) -> bool {
BlockEarlyErrorsContext::is_supported_var(kind)
}
fn is_strict<'alloc>(&self) -> Result<'alloc, bool> {
Err(ParseError::NotImplemented("strict-mode-only early error is not yet supported").into())
}
}
impl LexicalEarlyErrorsContext for CaseBlockEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_lexical(kind));
// Static Semantics: Early Errors
//
// SwitchStatement : switch ( Expression ) CaseBlock
//
// * It is a Syntax Error if the LexicallyDeclaredNames of CaseBlock
// contains any duplicate entries.
if let Some(info) = self.lex_names_of_case_block.get(&name) {
// Changes to switch Statement Static Semantics: Early Errors
//
// SwitchStatement : switch ( Expression ) CaseBlock
//
// * It is a Syntax Error if the LexicallyDeclaredNames of
// CaseBlock contains any duplicate entries, ** unless the source
// code matching this production is not strict mode code and the
// duplicate entries are only bound by FunctionDeclarations **.
if !(!self.is_strict()?
&& info.kind == DeclarationKind::LexicalFunction
&& kind == DeclarationKind::LexicalFunction)
{
let name = atoms.get(name);
return Err(ParseError::DuplicateBinding(
name,
info.kind,
info.offset,
kind,
offset,
)
.into());
}
}
// Static Semantics: Early Errors
//
// SwitchStatement : switch ( Expression ) CaseBlock
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of CaseBlock also occurs in the VarDeclaredNames of CaseBlock.
if let Some(info) = self.var_names_of_case_block.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.lex_names_of_case_block
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl VarEarlyErrorsContext for CaseBlockEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_var(kind));
// Static Semantics: Early Errors
//
// SwitchStatement : switch ( Expression ) CaseBlock
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of CaseBlock also occurs in the VarDeclaredNames of CaseBlock.
if let Some(info) = self.lex_names_of_case_block.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.var_names_of_case_block
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
// ===========================================================================
// The try Statement
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct CatchParameterEarlyErrorsContext {
bound_names_of_catch_param: HashMap<SourceAtomSetIndex, DeclarationInfo>,
is_simple: bool,
}
impl CatchParameterEarlyErrorsContext {
pub fn new_with_binding_identifier() -> Self {
Self {
bound_names_of_catch_param: HashMap::new(),
is_simple: true,
}
}
pub fn new_with_binding_pattern() -> Self {
Self {
bound_names_of_catch_param: HashMap::new(),
is_simple: false,
}
}
}
impl ParameterEarlyErrorsContext for CatchParameterEarlyErrorsContext {
fn declare<'alloc>(
&mut self,
name: SourceAtomSetIndex,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// BoundNames of CatchParameter
//
// CatchParameter => BindingIdentifier
// CatchParameter => BindingPattern
let kind = DeclarationKind::CatchParameter;
// Static Semantics: Early Errors
//
// Catch : catch ( CatchParameter ) Block
//
// * It is a Syntax Error if BoundNames of CatchParameter contains any
// duplicate elements.
if let Some(info) = self.bound_names_of_catch_param.get(&name) {
let name = atoms.get(name);
return Err(ParseError::DuplicateBinding(name, info.kind, offset, kind, offset).into());
}
self.bound_names_of_catch_param
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub struct CatchBlockEarlyErrorsContext {
param: CatchParameterEarlyErrorsContext,
block: BlockEarlyErrorsContext,
}
impl CatchBlockEarlyErrorsContext {
pub fn new(param: CatchParameterEarlyErrorsContext) -> Self {
Self {
param,
block: BlockEarlyErrorsContext::new(),
}
}
}
impl LexicalEarlyErrorsContext for CatchBlockEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// Catch : catch ( CatchParameter ) Block
//
// * It is a Syntax Error if any element of the BoundNames of
// CatchParameter also occurs in the LexicallyDeclaredNames of Block.
if let Some(info) = self.param.bound_names_of_catch_param.get(&name) {
let name = atoms.get(name);
return Err(ParseError::DuplicateBinding(name, info.kind, offset, kind, offset).into());
}
self.block.declare_lex(name, kind, offset, atoms)
}
}
impl VarEarlyErrorsContext for CatchBlockEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// Catch : catch ( CatchParameter ) Block
//
// * It is a Syntax Error if any element of the BoundNames of
// CatchParameter also occurs in the VarDeclaredNames of Block.
//
if let Some(info) = self.param.bound_names_of_catch_param.get(&name) {
// VariableStatements in Catch Blocks
//
// Catch : catch ( CatchParameter ) Block
//
// * It is a Syntax Error if any element of the BoundNames of
// CatchParameter also occurs in the VarDeclaredNames of Block **
// unless CatchParameter is CatchParameter : BindingIdentifier **.
if !self.param.is_simple {
let name = atoms.get(name);
return Err(ParseError::DuplicateBinding(
name,
info.kind,
info.offset,
kind,
offset,
)
.into());
}
}
self.block.declare_var(name, kind, offset, atoms)
}
}
// ===========================================================================
// Function Definitions
//
// Arrow Function Definitions
//
// Method Definitions
//
// Generator Function Definitions
//
// Async Generator Function Definitions
//
// Async Function Definitions
//
// Async Arrow Function Definitions
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct FormalParametersEarlyErrorsContext {
bound_names_of_params: HashMap<SourceAtomSetIndex, DeclarationInfo>,
is_simple: bool,
}
impl FormalParametersEarlyErrorsContext {
pub fn new_simple() -> Self {
Self {
bound_names_of_params: HashMap::new(),
is_simple: true,
}
}
pub fn new_non_simple() -> Self {
Self {
bound_names_of_params: HashMap::new(),
is_simple: false,
}
}
}
impl ParameterEarlyErrorsContext for FormalParametersEarlyErrorsContext {
fn declare<'alloc>(
&mut self,
name: SourceAtomSetIndex,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// BoundNames of FormalParameterList
//
// Static Semantics: BoundNames
//
// FormalParameters => FunctionParameterList => FormalParameter
// => BindingElement => SingleNameBinding => BindingIdentifier
//
// and
//
// FormalParameters => FunctionParameterList => FormalParameter
// => BindingElement => BindingPattern
//
// and
//
// FormalParameters => FunctionRestParameter => BindingRestElement
// => BindingIdentifier
//
// and
//
// FormalParameters => FunctionRestParameter => BindingRestElement
// => BindingPattern
let kind = DeclarationKind::FormalParameter;
// Static Semantics: Early Errors
//
// FormalParameters : FormalParameterList
//
// * It is a Syntax Error if IsSimpleParameterList of
// FormalParameterList is false and BoundNames of FormalParameterList
// contains any duplicate elements.
if let Some(info) = self.bound_names_of_params.get(&name) {
if !self.is_simple {
let name = atoms.get(name);
return Err(ParseError::DuplicateBinding(
name,
info.kind,
info.offset,
kind,
offset,
)
.into());
}
}
self.bound_names_of_params
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub struct UniqueFormalParametersEarlyErrorsContext {
bound_names_of_params: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl UniqueFormalParametersEarlyErrorsContext {
pub fn new() -> Self {
Self {
bound_names_of_params: HashMap::new(),
}
}
}
impl ParameterEarlyErrorsContext for UniqueFormalParametersEarlyErrorsContext {
fn declare<'alloc>(
&mut self,
name: SourceAtomSetIndex,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
let kind = DeclarationKind::FormalParameter;
// Static Semantics: Early Errors
//
// UniqueFormalParameters : FormalParameters
//
// * It is a Syntax Error if BoundNames of FormalParameters contains any
// duplicate elements.
//
// and
//
// Static Semantics: Early Errors
//
// MethodDefinition :
// set PropertyName ( PropertySetParameterList ) { FunctionBody }
//
// * It is a Syntax Error if BoundNames of PropertySetParameterList
// contains any duplicate elements.
if let Some(info) = self.bound_names_of_params.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.bound_names_of_params
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
#[derive(Debug, PartialEq)]
struct InternalFunctionBodyEarlyErrorsContext {
lex_names_of_body: HashMap<SourceAtomSetIndex, DeclarationInfo>,
var_names_of_body: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl InternalFunctionBodyEarlyErrorsContext {
fn new() -> Self {
Self {
lex_names_of_body: HashMap::new(),
var_names_of_body: HashMap::new(),
}
}
fn is_supported_lexical(kind: DeclarationKind) -> bool {
match kind {
// LexicallyDeclaredNames of FunctionStatementList
//
// Static Semantics: LexicallyDeclaredNames
//
// FunctionStatementList
// 1. Return TopLevelLexicallyDeclaredNames of StatementList.
//
// Static Semantics: TopLevelLexicallyDeclaredNames
//
// StatementList => StatementListItem => Statement
// 1. Return a new empty List.
//
// StatementList => StatementListItem => Declaration
// 1. If Declaration is Declaration : HoistableDeclaration, then
// a. Return « ».
// 2. Return the BoundNames of Declaration.
//
// See Block::is_supported_lexical for the details.
DeclarationKind::Class | DeclarationKind::Let | DeclarationKind::Const => true,
_ => false,
}
}
fn is_supported_var(kind: DeclarationKind) -> bool {
match kind {
// VarDeclaredNames of FunctionStatementList
//
// Static Semantics: VarDeclaredNames
//
// FunctionStatementList
// 1. Return TopLevelVarDeclaredNames of StatementList.
//
// Static Semantics: TopLevelVarDeclaredNames
//
// StatementList => StatementListItem => Declaration
// 1. If Declaration is Declaration : HoistableDeclaration, then
// a. Return the BoundNames of HoistableDeclaration.
// 2. Return a new empty List.
//
// HoistableDeclaration => FunctionDeclaration
// 1. Return the BoundNames of BindingIdentifier.
// 1. Return « "*default*" ».
//
// and
//
// Static Semantics: TopLevelVarDeclaredNames
//
// StatementList => StatementListItem => Statement
// 1. If Statement is Statement : LabelledStatement, return
// TopLevelVarDeclaredNames of Statement.
//
// Static Semantics: TopLevelVarDeclaredNames
//
// LabelledStatement => LabelledItem => Statement
// 1. If Statement is Statement : LabelledStatement, return
// TopLevelVarDeclaredNames of Statement.
// 2. Return VarDeclaredNames of Statement.
//
// LabelledStatement => LabelledItem => FunctionDeclaration
// 1. Return BoundNames of FunctionDeclaration.
DeclarationKind::BodyLevelFunction |
// Static Semantics: TopLevelVarDeclaredNames
//
// StatementList => StatementListItem => Statement
// 2. Return VarDeclaredNames of Statement.
//
// and
//
// Static Semantics: TopLevelVarDeclaredNames
//
// StatementList => StatementListItem => Statement
// 1. If Statement is Statement : LabelledStatement, return
// TopLevelVarDeclaredNames of Statement.
//
// Static Semantics: TopLevelVarDeclaredNames
//
// LabelledStatement => LabelledItem => Statement
// 2. Return VarDeclaredNames of Statement.
//
// See Block::is_supported_var for the details.
DeclarationKind::Var |
DeclarationKind::VarForAnnexBLexicalFunction => true,
_ => false,
}
}
}
impl LexicalEarlyErrorsContext for InternalFunctionBodyEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_lexical(kind));
// Static Semantics: Early Errors
//
// FunctionBody : FunctionStatementList
//
// * It is a Syntax Error if the LexicallyDeclaredNames of
// FunctionStatementList contains any duplicate entries.
if let Some(info) = self.lex_names_of_body.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
// Static Semantics: Early Errors
//
// FunctionBody : FunctionStatementList
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of FunctionStatementList also occurs in the VarDeclaredNames of
// FunctionStatementList.
if let Some(info) = self.var_names_of_body.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.lex_names_of_body
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl VarEarlyErrorsContext for InternalFunctionBodyEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_var(kind));
// Static Semantics: Early Errors
//
// FunctionBody : FunctionStatementList
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of FunctionStatementList also occurs in the VarDeclaredNames of
// FunctionStatementList.
if let Some(info) = self.lex_names_of_body.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.var_names_of_body
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl ControlEarlyErrorsContext for InternalFunctionBodyEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc> {
ModuleScriptOrFunctionEarlyErrorsContext::on_unhandled_break_or_continue(info)
}
}
// Functions with FormalParameters + FunctionBody.
//
// This is used for the following:
// * function declaration
// * function expression
// * generator declaration
// * generator expression
// * async generator declaration
// * async generator expression
// * async function declaration
// * async function expression
#[derive(Debug, PartialEq)]
pub struct FunctionBodyEarlyErrorsContext {
param: FormalParametersEarlyErrorsContext,
body: InternalFunctionBodyEarlyErrorsContext,
}
impl FunctionBodyEarlyErrorsContext {
pub fn new(param: FormalParametersEarlyErrorsContext) -> Self {
Self {
param,
body: InternalFunctionBodyEarlyErrorsContext::new(),
}
}
}
impl LexicalEarlyErrorsContext for FunctionBodyEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// FunctionDeclaration :
// function BindingIdentifier ( FormalParameters ) { FunctionBody }
// FunctionDeclaration :
// function ( FormalParameters ) { FunctionBody }
// FunctionExpression :
// function BindingIdentifier_opt ( FormalParameters )
// { FunctionBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// FormalParameters also occurs in the LexicallyDeclaredNames of
// FunctionBody.
//
// and
//
// Static Semantics: Early Errors
//
// GeneratorDeclaration :
// function * BindingIdentifier ( FormalParameters ) { GeneratorBody }
// GeneratorDeclaration :
// function * ( FormalParameters ) { GeneratorBody }
// GeneratorExpression :
// function * BindingIdentifier_opt ( FormalParameters )
// { GeneratorBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// FormalParameters also occurs in the LexicallyDeclaredNames of
// GeneratorBody.
//
// and
//
// Static Semantics: Early Errors
//
// AsyncGeneratorDeclaration :
// async function * BindingIdentifier ( FormalParameters )
// { AsyncGeneratorBody }
// AsyncGeneratorDeclaration :
// async function * ( FormalParameters ) { AsyncGeneratorBody }
// AsyncGeneratorExpression :
// async function * BindingIdentifier_opt ( FormalParameters )
// { AsyncGeneratorBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// FormalParameters also occurs in the LexicallyDeclaredNames of
// AsyncGeneratorBody.
//
// and
//
// Static Semantics: Early Errors
//
// AsyncFunctionDeclaration :
// async function BindingIdentifier ( FormalParameters )
// { AsyncFunctionBody }
// AsyncFunctionDeclaration :
// async function ( FormalParameters ) { AsyncFunctionBody }
// AsyncFunctionExpression :
// async function ( FormalParameters ) { AsyncFunctionBody }
// AsyncFunctionExpression :
// async function BindingIdentifier ( FormalParameters )
// { AsyncFunctionBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// FormalParameters also occurs in the LexicallyDeclaredNames of
// AsyncFunctionBody.
if let Some(info) = self.param.bound_names_of_params.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.body.declare_lex(name, kind, offset, atoms)
}
}
impl VarEarlyErrorsContext for FunctionBodyEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
self.body.declare_var(name, kind, offset, atoms)
}
}
impl ControlEarlyErrorsContext for FunctionBodyEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc> {
self.body.on_unhandled_break_or_continue(info)
}
}
// Functions with UniqueFormalParameters + FunctionBody
//
// This is used for the following:
// * arrow function
// * method definition
// * setter
// * generator method
// * async generator method
// * async method
// * async arrow function
#[derive(Debug, PartialEq)]
pub struct UniqueFunctionBodyEarlyErrorsContext {
param: UniqueFormalParametersEarlyErrorsContext,
body: InternalFunctionBodyEarlyErrorsContext,
}
impl UniqueFunctionBodyEarlyErrorsContext {
pub fn new(param: UniqueFormalParametersEarlyErrorsContext) -> Self {
Self {
param,
body: InternalFunctionBodyEarlyErrorsContext::new(),
}
}
}
impl LexicalEarlyErrorsContext for UniqueFunctionBodyEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// ArrowFunction : ArrowParameters => ConciseBody
//
// * It is a Syntax Error if any element of the BoundNames of
// ArrowParameters also occurs in the LexicallyDeclaredNames of
// ConciseBody.
//
// and
//
// Static Semantics: Early Errors
//
// MethodDefinition :
// PropertyName ( UniqueFormalParameters ) { FunctionBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// UniqueFormalParameters also occurs in the LexicallyDeclaredNames of
// FunctionBody.
//
// MethodDefinition :
// set PropertyName ( PropertySetParameterList ) { FunctionBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// PropertySetParameterList also occurs in the LexicallyDeclaredNames
// of FunctionBody.
//
// and
//
// Static Semantics: Early Errors
//
// GeneratorMethod :
// * PropertyName ( UniqueFormalParameters ) { GeneratorBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// UniqueFormalParameters also occurs in the LexicallyDeclaredNames of
// GeneratorBody.
//
// and
//
// Static Semantics: Early Errors
//
// AsyncGeneratorMethod :
// async * PropertyName ( UniqueFormalParameters )
// { AsyncGeneratorBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// UniqueFormalParameters also occurs in the LexicallyDeclaredNames of
// AsyncGeneratorBody.
//
// and
//
// Static Semantics: Early Errors
//
// AsyncMethod :
// async PropertyName ( UniqueFormalParameters ) { AsyncFunctionBody }
//
// * It is a Syntax Error if any element of the BoundNames of
// UniqueFormalParameters also occurs in the LexicallyDeclaredNames of
// AsyncFunctionBody.
//
// and
//
// Static Semantics: Early Errors
//
// AsyncArrowFunction :
// async AsyncArrowBindingIdentifier => AsyncConciseBody
//
// * It is a Syntax Error if any element of the BoundNames of
// AsyncArrowBindingIdentifier also occurs in the
// LexicallyDeclaredNames of AsyncConciseBody.
//
// AsyncArrowFunction :
// CoverCallExpressionAndAsyncArrowHead => AsyncConciseBody
//
// * It is a Syntax Error if any element of the BoundNames of
// CoverCallExpressionAndAsyncArrowHead also occurs in the
// LexicallyDeclaredNames of AsyncConciseBody.
if let Some(info) = self.param.bound_names_of_params.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.body.declare_lex(name, kind, offset, atoms)
}
}
impl VarEarlyErrorsContext for UniqueFunctionBodyEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
self.body.declare_var(name, kind, offset, atoms)
}
}
impl ControlEarlyErrorsContext for UniqueFunctionBodyEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc> {
self.body.on_unhandled_break_or_continue(info)
}
}
// ===========================================================================
// Scripts
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct ScriptEarlyErrorsContext {
lex_names_of_body: HashMap<SourceAtomSetIndex, DeclarationInfo>,
var_names_of_body: HashMap<SourceAtomSetIndex, DeclarationInfo>,
}
impl ScriptEarlyErrorsContext {
pub fn new() -> Self {
Self {
lex_names_of_body: HashMap::new(),
var_names_of_body: HashMap::new(),
}
}
fn is_supported_lexical(kind: DeclarationKind) -> bool {
match kind {
// LexicallyDeclaredNames of ScriptBody
//
// Static Semantics: LexicallyDeclaredNames
//
// ScriptBody => StatementList
// 1. Return TopLevelLexicallyDeclaredNames of StatementList.
// StatementList => StatementListItem => Declaration
// 1. If Declaration is Declaration : HoistableDeclaration, then
// a. Return « ».
// 2. Return the BoundNames of Declaration.
//
// See Block::is_supported_lexical for the details.
DeclarationKind::Class | DeclarationKind::Let | DeclarationKind::Const => true,
_ => false,
}
}
fn is_supported_var(kind: DeclarationKind) -> bool {
match kind {
// VarDeclaredNames of ScriptBody
//
// Static Semantics: VarDeclaredNames
//
// ScriptBody => StatementList
// 1. Return TopLevelVarDeclaredNames of StatementList.
//
// See Block::is_supported_var for the detail.
DeclarationKind::Var
| DeclarationKind::BodyLevelFunction
| DeclarationKind::VarForAnnexBLexicalFunction => true,
_ => false,
}
}
}
impl LexicalEarlyErrorsContext for ScriptEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_lexical(kind));
// Static Semantics: Early Errors
//
// Script : ScriptBody
//
// * It is a Syntax Error if the LexicallyDeclaredNames of ScriptBody
// contains any duplicate entries.
if let Some(info) = self.lex_names_of_body.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
// Static Semantics: Early Errors
//
// Script : ScriptBody
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of ScriptBody also occurs in the VarDeclaredNames of ScriptBody.
if let Some(info) = self.var_names_of_body.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.lex_names_of_body
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl VarEarlyErrorsContext for ScriptEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_var(kind));
// Static Semantics: Early Errors
//
// Script : ScriptBody
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of ScriptBody also occurs in the VarDeclaredNames of ScriptBody.
if let Some(info) = self.lex_names_of_body.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.var_names_of_body
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl ControlEarlyErrorsContext for ScriptEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc> {
ModuleScriptOrFunctionEarlyErrorsContext::on_unhandled_break_or_continue(info)
}
}
// ===========================================================================
// Modules
// ===========================================================================
#[derive(Debug, PartialEq)]
pub struct ModuleEarlyErrorsContext {
lex_names_of_item_list: HashMap<SourceAtomSetIndex, DeclarationInfo>,
var_names_of_item_list: HashMap<SourceAtomSetIndex, DeclarationInfo>,
exported_names_of_item_list: HashMap<SourceAtomSetIndex, usize>,
exported_bindings_of_item_list: HashMap<SourceAtomSetIndex, usize>,
}
impl ModuleEarlyErrorsContext {
pub fn new() -> Self {
Self {
lex_names_of_item_list: HashMap::new(),
var_names_of_item_list: HashMap::new(),
exported_names_of_item_list: HashMap::new(),
exported_bindings_of_item_list: HashMap::new(),
}
}
fn is_supported_lexical(kind: DeclarationKind) -> bool {
match kind {
// LexicallyDeclaredNames of ModuleItemList
//
// Static Semantics: LexicallyDeclaredNames
//
// ModuleItemList => ModuleItem => ImportDeclaration
// 1. Return the BoundNames of ImportDeclaration.
//
// ImportDeclaration ... => ImportedBinding => BindingIdentifier
DeclarationKind::Import |
// ModuleItemList => ModuleItem => ExportDeclaration
// 1. If ExportDeclaration is export VariableStatement, return a
// new empty List.
// 2. Return the BoundNames of ExportDeclaration.
//
// ExportDeclaration => Declaration
// 1. Return the BoundNames of Declaration.
// ExportDeclaration => HoistableDeclaration
// 1. Let declarationNames be the BoundNames of
// HoistableDeclaration.
// 2. If declarationNames does not include the element
// "*default*", append "*default*" to declarationNames.
// 3. Return declarationNames.
// ExportDeclaration => ClassDeclaration
// 1. Let declarationNames be the BoundNames of ClassDeclaration.
// 2. If declarationNames does not include the element
// "*default*", append "*default*" to declarationNames.
// 3. Return declarationNames.
// ExportDeclaration => AssignmentExpression
// 1. Return « "*default*" ».
// ModuleItemList => ModuleItem => StatementListItem
//
// See Block::is_supported_lexical for the details.
//
// Function declaration in the top level of module script is
// lexical, but here isn't LexicalFunction/LexicalAsyncOrGenerator
// distinction because B.3.3.4 doesn't apply to Module.
DeclarationKind::BodyLevelFunction |
DeclarationKind::Class |
DeclarationKind::Let |
DeclarationKind::Const => true,
_ => false,
}
}
fn is_supported_var(kind: DeclarationKind) -> bool {
match kind {
// VarDeclaredNames of ModuleItemList
//
// Static Semantics: VarDeclaredNames
//
// ModuleItemList => ModuleItem => ImportDeclaration
// 1. Return a new empty List.
// ModuleItemList => ModuleItem => ExportDeclaration
// 1. If ExportDeclaration is export VariableStatement, return
// BoundNames of ExportDeclaration.
// 2. Return a new empty List.
//
// and
//
// ModuleItemList => ModuleItem => StatementList
DeclarationKind::Var => true,
_ => false,
}
}
#[allow(dead_code)]
pub fn add_exported_name<'alloc>(
&mut self,
name: SourceAtomSetIndex,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if the ExportedNames of ModuleItemList
// contains any duplicate entries.
if let Some(prev_offset) = self.exported_names_of_item_list.get(&name) {
let name = atoms.get(name);
return Err(ParseError::DuplicateExport(name, prev_offset.clone(), offset).into());
}
self.exported_names_of_item_list.insert(name, offset);
Ok(())
}
#[allow(dead_code)]
pub fn add_exported_binding(&mut self, name: SourceAtomSetIndex, offset: usize) {
self.exported_bindings_of_item_list.insert(name, offset);
}
#[allow(dead_code)]
pub fn check_exported_name<'alloc>(
&self,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
// Static Semantics: Early Errors
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if any element of the ExportedBindings of
// ModuleItemList does not also occur in either the VarDeclaredNames
// of ModuleItemList, or the LexicallyDeclaredNames of ModuleItemList.
for (name, offset) in &self.exported_bindings_of_item_list {
if !self.var_names_of_item_list.contains_key(name)
&& !self.lex_names_of_item_list.contains_key(name)
{
let name = atoms.get(*name);
return Err(ParseError::MissingExport(name, offset.clone()).into());
}
}
Ok(())
}
}
impl LexicalEarlyErrorsContext for ModuleEarlyErrorsContext {
fn declare_lex<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_lexical(kind));
// Static Semantics: Early Errors
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if the LexicallyDeclaredNames of
// ModuleItemList contains any duplicate entries.
//
// and
//
// Static Semantics: Early Errors
//
// ModuleItem : ImportDeclaration
//
// * It is a Syntax Error if the BoundNames of ImportDeclaration
// contains any duplicate entries.
if let Some(info) = self.lex_names_of_item_list.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
// Static Semantics: Early Errors
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of ModuleItemList also occurs in the VarDeclaredNames of
// ModuleItemList.
if let Some(info) = self.var_names_of_item_list.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.lex_names_of_item_list
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl VarEarlyErrorsContext for ModuleEarlyErrorsContext {
fn declare_var<'alloc>(
&mut self,
name: SourceAtomSetIndex,
kind: DeclarationKind,
offset: usize,
atoms: &SourceAtomSet<'alloc>,
) -> EarlyErrorsResult<'alloc> {
debug_assert!(Self::is_supported_var(kind));
// Static Semantics: Early Errors
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if any element of the LexicallyDeclaredNames
// of ModuleItemList also occurs in the VarDeclaredNames of
// ModuleItemList.
if let Some(info) = self.lex_names_of_item_list.get(&name) {
let name = atoms.get(name);
return Err(
ParseError::DuplicateBinding(name, info.kind, info.offset, kind, offset).into(),
);
}
self.var_names_of_item_list
.insert(name, DeclarationInfo::new(kind, offset));
Ok(())
}
}
impl ControlEarlyErrorsContext for ModuleEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(
&self,
info: &ControlInfo,
) -> EarlyErrorsResult<'alloc> {
ModuleScriptOrFunctionEarlyErrorsContext::on_unhandled_break_or_continue(info)
}
}
struct ModuleScriptOrFunctionEarlyErrorsContext {}
impl ModuleScriptOrFunctionEarlyErrorsContext {
fn on_unhandled_break_or_continue<'alloc>(info: &ControlInfo) -> EarlyErrorsResult<'alloc> {
if let Some(_) = info.label {
match info.kind {
// Static Semantics: Early Errors
//
// Script : ScriptBody
//
// * It is a Syntax Error if ContainsUndefinedContinueTarget of StatementList
// with arguments « » and « » is true.
//
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if ContainsUndefinedContinueTarget of ModuleItemList
// with arguments « » and « » is true.
ControlKind::Continue => {
return Err(ParseError::BadContinue.into());
}
// Static Semantics: Early Errors
//
// Script : ScriptBody
//
// * It is a Syntax Error if ContainsUndefinedBreakTarget of StatementList
// with argument « » is true.
//
//
// ModuleBody : ModuleItemList
//
// * It is a Syntax Error if ContainsUndefinedBreakTarget of ModuleItemList
// with argument « » is true.
ControlKind::Break => {
return Err(ParseError::LabelNotFound.into());
}
}
} else {
match info.kind {
// Static Semantics: Early Errors
//
// ContinueStatement : continue ;
// ContinueStatement : continueLabelIdentifier ;
//
// * It is a Syntax Error if this ContinueStatement is not nested, directly or
// indirectly (but not crossing function boundaries), within an
// IterationStatement.
ControlKind::Continue => {
return Err(ParseError::BadContinue.into());
}
// Static Semantics: Early Errors
//
// BreakStatement : break ;
//
// * It is a Syntax Error if this BreakStatement is not nested, directly or
// indirectly (but not crossing function boundaries), within an
// IterationStatement or a SwitchStatement.
ControlKind::Break => {
return Err(ParseError::ToughBreak.into());
}
}
}
}
}