use core::ops::ControlFlow;
use std::convert::Infallible;
use boa_interner::{Interner, Sym};
use rustc_hash::FxHashSet;
use crate::{
Declaration, Expression, LinearSpan, ModuleItem, Script, Statement, StatementList,
StatementListItem,
declaration::{
Binding, ExportDeclaration, ImportDeclaration, LexicalDeclaration, VarDeclaration, Variable,
},
expression::{
Await, Call, Identifier, NewTarget, OptionalOperationKind, SuperCall, This, Yield,
access::{PrivatePropertyAccess, SuperPropertyAccess},
literal::PropertyDefinition,
operator::BinaryInPrivate,
},
function::{
ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,
AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,
ClassElementName, ClassExpression, FormalParameterList, FunctionBody, FunctionDeclaration,
FunctionExpression, GeneratorDeclaration, GeneratorExpression, PrivateFieldDefinition,
},
statement::{
LabelledItem, With,
iteration::{ForLoopInitializer, IterableLoopInitializer},
},
visitor::{NodeRef, VisitWith, Visitor},
};
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContainsSymbol {
Super,
SuperProperty,
SuperCall,
YieldExpression,
AwaitExpression,
NewTarget,
ClassBody,
ClassHeritage,
This,
MethodDefinition,
EvalOrArguments,
DirectEval,
}
#[must_use]
pub fn contains<N>(node: &N, symbol: ContainsSymbol) -> bool
where
N: VisitWith,
{
#[derive(Debug, Clone, Copy)]
struct ContainsVisitor(ContainsSymbol);
impl ContainsVisitor {
fn visit_contains_eval(&mut self, contains_direct_eval: bool) -> ControlFlow<()> {
if self.0 == ContainsSymbol::DirectEval && contains_direct_eval {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
impl<'ast> Visitor<'ast> for ContainsVisitor {
type BreakTy = ();
fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {
self.visit_expression(node.expression())?;
node.statement().visit_with(self)
}
fn visit_call(&mut self, node: &'ast Call) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::DirectEval
&& let Expression::Identifier(ident) = node.function().flatten()
&& ident.sym() == Sym::EVAL
{
return ControlFlow::Break(());
}
self.visit_expression(node.function())?;
for arg in node.args() {
self.visit_expression(arg)?;
}
ControlFlow::Continue(())
}
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::EvalOrArguments
&& (node.sym() == Sym::EVAL || node.sym() == Sym::ARGUMENTS)
{
return ControlFlow::Break(());
}
ControlFlow::Continue(())
}
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_contains_eval(node.contains_direct_eval)?;
ControlFlow::Continue(())
}
fn visit_class_expression(
&mut self,
node: &'ast ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {
return ControlFlow::Break(());
}
if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_class_declaration(
&mut self,
node: &'ast ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {
return ControlFlow::Break(());
}
if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
match node {
ClassElement::MethodDefinition(m) => {
if self.0 == ContainsSymbol::DirectEval {
return ControlFlow::Continue(());
}
if let ClassElementName::PropertyName(name) = m.name() {
name.visit_with(self)
} else {
ControlFlow::Continue(())
}
}
ClassElement::FieldDefinition(field)
| ClassElement::StaticFieldDefinition(field) => field.name.visit_with(self),
_ => ControlFlow::Continue(()),
}
}
fn visit_property_definition(
&mut self,
node: &'ast PropertyDefinition,
) -> ControlFlow<Self::BreakTy> {
if let PropertyDefinition::MethodDefinition(m) = node {
if self.0 == ContainsSymbol::DirectEval {
return ControlFlow::Continue(());
}
if self.0 == ContainsSymbol::MethodDefinition {
return ControlFlow::Break(());
}
return m.name().visit_with(self);
}
node.visit_with(self)
}
fn visit_arrow_function(
&mut self,
node: &'ast ArrowFunction,
) -> ControlFlow<Self::BreakTy> {
if ![
ContainsSymbol::NewTarget,
ContainsSymbol::SuperProperty,
ContainsSymbol::SuperCall,
ContainsSymbol::Super,
ContainsSymbol::This,
ContainsSymbol::DirectEval,
]
.contains(&self.0)
{
return ControlFlow::Continue(());
}
node.visit_with(self)
}
fn visit_async_arrow_function(
&mut self,
node: &'ast AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
if ![
ContainsSymbol::NewTarget,
ContainsSymbol::SuperProperty,
ContainsSymbol::SuperCall,
ContainsSymbol::Super,
ContainsSymbol::This,
ContainsSymbol::DirectEval,
]
.contains(&self.0)
{
return ControlFlow::Continue(());
}
node.visit_with(self)
}
fn visit_super_property_access(
&mut self,
node: &'ast SuperPropertyAccess,
) -> ControlFlow<Self::BreakTy> {
if [ContainsSymbol::SuperProperty, ContainsSymbol::Super].contains(&self.0) {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_super_call(&mut self, node: &'ast SuperCall) -> ControlFlow<Self::BreakTy> {
if [ContainsSymbol::SuperCall, ContainsSymbol::Super].contains(&self.0) {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_yield(&mut self, node: &'ast Yield) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::YieldExpression {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_await(&mut self, node: &'ast Await) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::AwaitExpression {
return ControlFlow::Break(());
}
node.visit_with(self)
}
fn visit_this(&mut self, _node: &'ast This) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::This {
return ControlFlow::Break(());
}
ControlFlow::Continue(())
}
fn visit_new_target(&mut self, _node: &'ast NewTarget) -> ControlFlow<Self::BreakTy> {
if self.0 == ContainsSymbol::NewTarget {
return ControlFlow::Break(());
}
ControlFlow::Continue(())
}
}
node.visit_with(&mut ContainsVisitor(symbol)).is_break()
}
#[must_use]
pub fn contains_arguments<N>(node: &N) -> bool
where
N: VisitWith,
{
#[derive(Debug, Clone, Copy)]
struct ContainsArgsVisitor;
impl<'ast> Visitor<'ast> for ContainsArgsVisitor {
type BreakTy = ();
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
if node.sym() == Sym::ARGUMENTS {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
fn visit_function_expression(
&mut self,
_: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_function_declaration(
&mut self,
_: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function_expression(
&mut self,
_: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_function_declaration(
&mut self,
_: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator_expression(
&mut self,
_: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_generator_declaration(
&mut self,
_: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator_expression(
&mut self,
_: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_async_generator_declaration(
&mut self,
_: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
if let ClassElement::MethodDefinition(m) = node
&& let ClassElementName::PropertyName(name) = m.name()
{
return name.visit_with(self);
}
node.visit_with(self)
}
fn visit_property_definition(
&mut self,
node: &'ast PropertyDefinition,
) -> ControlFlow<Self::BreakTy> {
if let PropertyDefinition::MethodDefinition(m) = node {
m.name().visit_with(self)
} else {
node.visit_with(self)
}
}
}
node.visit_with(&mut ContainsArgsVisitor).is_break()
}
#[must_use]
#[inline]
pub fn has_direct_super_new(params: &FormalParameterList, body: &FunctionBody) -> bool {
contains(params, ContainsSymbol::SuperCall) || contains(body, ContainsSymbol::SuperCall)
}
pub(crate) trait IdentList {
fn add(&mut self, value: Sym, function: bool);
}
impl IdentList for Vec<Sym> {
fn add(&mut self, value: Sym, _function: bool) {
self.push(value);
}
}
impl IdentList for Vec<(Sym, bool)> {
fn add(&mut self, value: Sym, function: bool) {
self.push((value, function));
}
}
impl IdentList for FxHashSet<Sym> {
fn add(&mut self, value: Sym, _function: bool) {
self.insert(value);
}
}
#[derive(Debug)]
pub(crate) struct BoundNamesVisitor<'a, T: IdentList>(pub(crate) &'a mut T);
impl<'ast, T: IdentList> Visitor<'ast> for BoundNamesVisitor<'_, T> {
type BreakTy = Infallible;
fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
self.0.add(node.sym(), false);
ControlFlow::Continue(())
}
fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), true);
}
ControlFlow::Continue(())
}
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), true);
ControlFlow::Continue(())
}
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_class_expression(
&mut self,
node: &'ast ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(ident) = node.name() {
self.0.add(ident.sym(), false);
}
ControlFlow::Continue(())
}
fn visit_class_declaration(
&mut self,
node: &'ast ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.0.add(node.name().sym(), false);
ControlFlow::Continue(())
}
fn visit_export_declaration(
&mut self,
node: &'ast ExportDeclaration,
) -> ControlFlow<Self::BreakTy> {
match node {
ExportDeclaration::VarStatement(var) => self.visit_var_declaration(var)?,
ExportDeclaration::Declaration(decl) => self.visit_declaration(decl)?,
ExportDeclaration::DefaultFunctionDeclaration(f) => {
self.0.add(f.name().sym(), true);
}
ExportDeclaration::DefaultGeneratorDeclaration(g) => {
self.0.add(g.name().sym(), false);
}
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {
self.0.add(af.name().sym(), false);
}
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {
self.0.add(ag.name().sym(), false);
}
ExportDeclaration::DefaultClassDeclaration(cl) => {
self.0.add(cl.name().sym(), false);
}
ExportDeclaration::DefaultAssignmentExpression(_) => {
self.0.add(Sym::DEFAULT_EXPORT, false);
}
ExportDeclaration::ReExport { .. } | ExportDeclaration::List(_) => {}
}
ControlFlow::Continue(())
}
}
#[must_use]
pub fn bound_names<'a, N>(node: &'a N) -> Vec<Sym>
where
&'a N: Into<NodeRef<'a>>,
{
let mut names = Vec::new();
let _ = BoundNamesVisitor(&mut names).visit(node.into());
names
}
#[derive(Debug)]
struct LexicallyDeclaredNamesVisitor<'a, T: IdentList>(&'a mut T);
impl<'ast, T: IdentList> Visitor<'ast> for LexicallyDeclaredNamesVisitor<'_, T> {
type BreakTy = Infallible;
fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {
top_level_lexicals(node.statements(), self.0);
ControlFlow::Continue(())
}
fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {
top_level_lexicals(node.statement_list(), self.0);
ControlFlow::Continue(())
}
fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
match node {
ModuleItem::ImportDeclaration(import) => {
BoundNamesVisitor(self.0).visit_import_declaration(import)
}
ModuleItem::ExportDeclaration(export) => {
if matches!(export.as_ref(), ExportDeclaration::VarStatement(_)) {
ControlFlow::Continue(())
} else {
BoundNamesVisitor(self.0).visit_export_declaration(export)
}
}
ModuleItem::StatementListItem(item) => {
self.visit_statement_list_item(item)
}
}
}
fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
if let Statement::Labelled(labelled) = node {
return self.visit_labelled(labelled);
}
ControlFlow::Continue(())
}
fn visit_declaration(&mut self, node: &'ast Declaration) -> ControlFlow<Self::BreakTy> {
BoundNamesVisitor(self.0).visit_declaration(node)
}
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::FunctionDeclaration(f) => {
BoundNamesVisitor(self.0).visit_function_declaration(f)
}
LabelledItem::Statement(_) => ControlFlow::Continue(()),
}
}
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_arrow_function(&mut self, node: &'ast ArrowFunction) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_arrow_function(
&mut self,
node: &'ast AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
if let ClassElement::StaticBlock(block) = node {
self.visit_function_body(&block.body)?;
}
ControlFlow::Continue(())
}
fn visit_import_declaration(
&mut self,
node: &'ast ImportDeclaration,
) -> ControlFlow<Self::BreakTy> {
BoundNamesVisitor(self.0).visit_import_declaration(node)
}
fn visit_export_declaration(
&mut self,
node: &'ast ExportDeclaration,
) -> ControlFlow<Self::BreakTy> {
if matches!(node, ExportDeclaration::VarStatement(_)) {
return ControlFlow::Continue(());
}
BoundNamesVisitor(self.0).visit_export_declaration(node)
}
}
#[must_use]
pub fn lexically_declared_names<'a, N>(node: &'a N) -> Vec<Sym>
where
&'a N: Into<NodeRef<'a>>,
{
let mut names = Vec::new();
let _ = LexicallyDeclaredNamesVisitor(&mut names).visit(node.into());
names
}
#[must_use]
pub fn lexically_declared_names_legacy<'a, N>(node: &'a N) -> Vec<(Sym, bool)>
where
&'a N: Into<NodeRef<'a>>,
{
let mut names = Vec::new();
let _ = LexicallyDeclaredNamesVisitor(&mut names).visit(node.into());
names
}
#[derive(Debug)]
struct VarDeclaredNamesVisitor<'a>(&'a mut FxHashSet<Sym>);
impl<'ast> Visitor<'ast> for VarDeclaredNamesVisitor<'_> {
type BreakTy = Infallible;
fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {
top_level_vars(node.statements(), self.0);
ControlFlow::Continue(())
}
fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {
top_level_vars(node.statement_list(), self.0);
ControlFlow::Continue(())
}
fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
match node {
ModuleItem::ImportDeclaration(_) => {
ControlFlow::Continue(())
}
ModuleItem::ExportDeclaration(export) => {
if let ExportDeclaration::VarStatement(var) = export.as_ref() {
BoundNamesVisitor(self.0).visit_var_declaration(var)
} else {
ControlFlow::Continue(())
}
}
ModuleItem::StatementListItem(item) => self.visit_statement_list_item(item),
}
}
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
match node {
Statement::Empty
| Statement::Expression(_)
| Statement::Continue(_)
| Statement::Break(_)
| Statement::Return(_)
| Statement::Throw(_) => ControlFlow::Continue(()),
Statement::Block(node) => self.visit(node),
Statement::Var(node) => self.visit(node),
Statement::If(node) => self.visit(node),
Statement::DoWhileLoop(node) => self.visit(node),
Statement::WhileLoop(node) => self.visit(node),
Statement::ForLoop(node) => self.visit(node),
Statement::ForInLoop(node) => self.visit(node),
Statement::ForOfLoop(node) => self.visit(node),
Statement::Switch(node) => self.visit(node),
Statement::Labelled(node) => self.visit(node),
Statement::Try(node) => self.visit(node),
Statement::With(node) => self.visit(node),
}
}
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
match node {
StatementListItem::Statement(stmt) => self.visit_statement(stmt),
StatementListItem::Declaration(_) => ControlFlow::Continue(()),
}
}
fn visit_variable(&mut self, node: &'ast Variable) -> ControlFlow<Self::BreakTy> {
BoundNamesVisitor(self.0).visit_variable(node)
}
fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.else_node() {
self.visit(node)?;
}
self.visit(node.body())
}
fn visit_do_while_loop(
&mut self,
node: &'ast crate::statement::DoWhileLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())
}
fn visit_while_loop(
&mut self,
node: &'ast crate::statement::WhileLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())
}
fn visit_for_loop(
&mut self,
node: &'ast crate::statement::ForLoop,
) -> ControlFlow<Self::BreakTy> {
if let Some(ForLoopInitializer::Var(node)) = node.init() {
BoundNamesVisitor(self.0).visit_var_declaration(node)?;
}
self.visit(node.body())
}
fn visit_for_in_loop(
&mut self,
node: &'ast crate::statement::ForInLoop,
) -> ControlFlow<Self::BreakTy> {
if let IterableLoopInitializer::Var(node) = node.initializer() {
BoundNamesVisitor(self.0).visit_variable(node)?;
}
self.visit(node.body())
}
fn visit_for_of_loop(
&mut self,
node: &'ast crate::statement::ForOfLoop,
) -> ControlFlow<Self::BreakTy> {
if let IterableLoopInitializer::Var(node) = node.initializer() {
BoundNamesVisitor(self.0).visit_variable(node)?;
}
self.visit(node.body())
}
fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {
self.visit(node.statement())
}
fn visit_switch(&mut self, node: &'ast crate::statement::Switch) -> ControlFlow<Self::BreakTy> {
for case in node.cases() {
self.visit(case)?;
}
if let Some(node) = node.default() {
self.visit(node)?;
}
ControlFlow::Continue(())
}
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),
LabelledItem::Statement(stmt) => self.visit(stmt),
}
}
fn visit_try(&mut self, node: &'ast crate::statement::Try) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.finally() {
self.visit(node)?;
}
if let Some(node) = node.catch() {
self.visit(node.block())?;
}
self.visit(node.block())
}
fn visit_function_expression(
&mut self,
node: &'ast FunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_function_declaration(
&mut self,
node: &'ast FunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_function_expression(
&mut self,
node: &'ast AsyncFunctionExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_function_declaration(
&mut self,
node: &'ast AsyncFunctionDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_generator_expression(
&mut self,
node: &'ast GeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_generator_declaration(
&mut self,
node: &'ast GeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_generator_expression(
&mut self,
node: &'ast AsyncGeneratorExpression,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_async_generator_declaration(
&mut self,
node: &'ast AsyncGeneratorDeclaration,
) -> ControlFlow<Self::BreakTy> {
self.visit_function_body(node.body())
}
fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {
if let ClassElement::StaticBlock(block) = node {
self.visit_function_body(&block.body)?;
}
node.visit_with(self)
}
fn visit_import_declaration(
&mut self,
_: &'ast ImportDeclaration,
) -> ControlFlow<Self::BreakTy> {
ControlFlow::Continue(())
}
fn visit_export_declaration(
&mut self,
node: &'ast ExportDeclaration,
) -> ControlFlow<Self::BreakTy> {
match node {
ExportDeclaration::VarStatement(var) => {
BoundNamesVisitor(self.0).visit_var_declaration(var)
}
_ => ControlFlow::Continue(()),
}
}
}
#[must_use]
pub fn var_declared_names<'a, N>(node: &'a N) -> FxHashSet<Sym>
where
&'a N: Into<NodeRef<'a>>,
{
let mut names = FxHashSet::default();
let _ = VarDeclaredNamesVisitor(&mut names).visit(node.into());
names
}
fn top_level_lexicals<T: IdentList>(stmts: &StatementList, names: &mut T) {
for stmt in stmts.statements() {
if let StatementListItem::Declaration(decl) = stmt {
match decl.as_ref() {
Declaration::FunctionDeclaration(_)
| Declaration::GeneratorDeclaration(_)
| Declaration::AsyncFunctionDeclaration(_)
| Declaration::AsyncGeneratorDeclaration(_) => {}
Declaration::ClassDeclaration(class) => {
let _ = BoundNamesVisitor(names).visit_class_declaration(class);
}
Declaration::Lexical(decl) => {
let _ = BoundNamesVisitor(names).visit_lexical_declaration(decl);
}
}
}
}
}
fn top_level_vars(stmts: &StatementList, names: &mut FxHashSet<Sym>) {
for stmt in stmts.statements() {
match stmt {
StatementListItem::Declaration(decl) => {
match decl.as_ref() {
Declaration::FunctionDeclaration(f) => {
let _ = BoundNamesVisitor(names).visit_function_declaration(f);
}
Declaration::GeneratorDeclaration(f) => {
let _ = BoundNamesVisitor(names).visit_generator_declaration(f);
}
Declaration::AsyncFunctionDeclaration(f) => {
let _ = BoundNamesVisitor(names).visit_async_function_declaration(f);
}
Declaration::AsyncGeneratorDeclaration(f) => {
let _ = BoundNamesVisitor(names).visit_async_generator_declaration(f);
}
Declaration::ClassDeclaration(_) | Declaration::Lexical(_) => {}
}
}
StatementListItem::Statement(stmt) => {
let mut stmt = Some(stmt.as_ref());
while let Some(Statement::Labelled(labelled)) = stmt.as_ref() {
match labelled.item() {
LabelledItem::FunctionDeclaration(f) => {
let _ = BoundNamesVisitor(names).visit_function_declaration(f);
stmt = None;
}
LabelledItem::Statement(s) => stmt = Some(s),
}
}
if let Some(stmt) = stmt {
let _ = VarDeclaredNamesVisitor(names).visit(stmt);
}
}
}
}
}
#[must_use]
#[inline]
pub fn all_private_identifiers_valid<'a, N>(node: &'a N, private_names: Vec<Sym>) -> bool
where
&'a N: Into<NodeRef<'a>>,
{
AllPrivateIdentifiersValidVisitor(private_names)
.visit(node.into())
.is_continue()
}
struct AllPrivateIdentifiersValidVisitor(Vec<Sym>);
impl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {
type BreakTy = ();
fn visit_class_expression(
&mut self,
node: &'ast ClassExpression,
) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.super_ref() {
self.visit(node)?;
}
let mut names = self.0.clone();
for element in node.elements() {
match element {
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PrivateName(name) = m.name() {
names.push(name.description());
}
}
ClassElement::PrivateFieldDefinition(PrivateFieldDefinition { name, .. })
| ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {
name, ..
}) => {
names.push(name.description());
}
_ => {}
}
}
let mut visitor = Self(names);
if let Some(node) = node.constructor() {
visitor.visit(node)?;
}
for element in node.elements() {
match element {
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PropertyName(name) = m.name() {
visitor.visit(name)?;
}
visitor.visit(m.parameters())?;
visitor.visit(m.body())?;
}
ClassElement::FieldDefinition(field)
| ClassElement::StaticFieldDefinition(field) => {
visitor.visit(&field.name)?;
if let Some(expression) = &field.initializer {
visitor.visit(expression)?;
}
}
ClassElement::PrivateFieldDefinition(PrivateFieldDefinition {
initializer,
..
})
| ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {
initializer,
..
}) => {
if let Some(expression) = initializer {
visitor.visit(expression)?;
}
}
ClassElement::StaticBlock(block) => {
visitor.visit(&block.body)?;
}
}
}
ControlFlow::Continue(())
}
fn visit_class_declaration(
&mut self,
node: &'ast ClassDeclaration,
) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.super_ref() {
self.visit(node)?;
}
let mut names = self.0.clone();
for element in node.elements() {
match element {
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PrivateName(name) = m.name() {
names.push(name.description());
}
}
ClassElement::PrivateFieldDefinition(PrivateFieldDefinition { name, .. })
| ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {
name, ..
}) => {
names.push(name.description());
}
_ => {}
}
}
let mut visitor = Self(names);
if let Some(node) = node.constructor() {
visitor.visit(node)?;
}
for element in node.elements() {
match element {
ClassElement::MethodDefinition(m) => {
if let ClassElementName::PropertyName(name) = m.name() {
visitor.visit(name)?;
}
visitor.visit(m.parameters())?;
visitor.visit(m.body())?;
}
ClassElement::FieldDefinition(field)
| ClassElement::StaticFieldDefinition(field) => {
visitor.visit(&field.name)?;
if let Some(expression) = &field.initializer {
visitor.visit(expression)?;
}
}
ClassElement::PrivateFieldDefinition(PrivateFieldDefinition {
initializer,
..
})
| ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {
initializer,
..
}) => {
if let Some(expression) = initializer {
visitor.visit(expression)?;
}
}
ClassElement::StaticBlock(block) => {
visitor.visit(&block.body)?;
}
}
}
ControlFlow::Continue(())
}
fn visit_private_property_access(
&mut self,
node: &'ast PrivatePropertyAccess,
) -> ControlFlow<Self::BreakTy> {
if self.0.contains(&node.field().description()) {
self.visit(node.target())
} else {
ControlFlow::Break(())
}
}
fn visit_binary_in_private(
&mut self,
node: &'ast BinaryInPrivate,
) -> ControlFlow<Self::BreakTy> {
if self.0.contains(&node.lhs().description()) {
self.visit(node.rhs())
} else {
ControlFlow::Break(())
}
}
fn visit_optional_operation_kind(
&mut self,
node: &'ast OptionalOperationKind,
) -> ControlFlow<Self::BreakTy> {
match node {
OptionalOperationKind::SimplePropertyAccess { field } => {
self.visit_property_access_field(field)
}
OptionalOperationKind::PrivatePropertyAccess { field } => {
if self.0.contains(&field.description()) {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
}
OptionalOperationKind::Call { args } => {
for arg in args {
self.visit_expression(arg)?;
}
ControlFlow::Continue(())
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum CheckLabelsError {
DuplicateLabel(Sym),
UndefinedBreakTarget(Sym),
UndefinedContinueTarget(Sym),
IllegalBreakStatement,
IllegalContinueStatement,
}
impl CheckLabelsError {
#[must_use]
pub fn message(&self, interner: &Interner) -> String {
match self {
Self::DuplicateLabel(label) => {
format!("duplicate label: {}", interner.resolve_expect(*label))
}
Self::UndefinedBreakTarget(label) => {
format!(
"undefined break target: {}",
interner.resolve_expect(*label)
)
}
Self::UndefinedContinueTarget(label) => format!(
"undefined continue target: {}",
interner.resolve_expect(*label)
),
Self::IllegalBreakStatement => "illegal break statement".into(),
Self::IllegalContinueStatement => "illegal continue statement".into(),
}
}
}
pub fn check_labels<N>(node: &N) -> Result<(), CheckLabelsError>
where
N: VisitWith,
{
#[derive(Debug, Clone)]
struct CheckLabelsResolver {
labels: FxHashSet<Sym>,
continue_iteration_labels: FxHashSet<Sym>,
continue_labels: Option<FxHashSet<Sym>>,
iteration: bool,
switch: bool,
}
impl<'ast> Visitor<'ast> for CheckLabelsResolver {
type BreakTy = CheckLabelsError;
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
match node {
Statement::Block(node) => self.visit_block(node),
Statement::Var(_)
| Statement::Empty
| Statement::Expression(_)
| Statement::Return(_)
| Statement::Throw(_) => ControlFlow::Continue(()),
Statement::If(node) => self.visit_if(node),
Statement::DoWhileLoop(node) => self.visit_do_while_loop(node),
Statement::WhileLoop(node) => self.visit_while_loop(node),
Statement::ForLoop(node) => self.visit_for_loop(node),
Statement::ForInLoop(node) => self.visit_for_in_loop(node),
Statement::ForOfLoop(node) => self.visit_for_of_loop(node),
Statement::Switch(node) => self.visit_switch(node),
Statement::Labelled(node) => self.visit_labelled(node),
Statement::Try(node) => self.visit_try(node),
Statement::Continue(node) => self.visit_continue(node),
Statement::Break(node) => self.visit_break(node),
Statement::With(with) => self.visit_with(with),
}
}
fn visit_block(
&mut self,
node: &'ast crate::statement::Block,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
self.visit_statement_list(node.statement_list())?;
self.continue_labels = continue_labels;
ControlFlow::Continue(())
}
fn visit_break(
&mut self,
node: &'ast crate::statement::Break,
) -> ControlFlow<Self::BreakTy> {
if let Some(label) = node.label() {
if !self.labels.contains(&label) {
return ControlFlow::Break(CheckLabelsError::UndefinedBreakTarget(label));
}
} else if !self.iteration && !self.switch {
return ControlFlow::Break(CheckLabelsError::IllegalBreakStatement);
}
ControlFlow::Continue(())
}
fn visit_continue(
&mut self,
node: &'ast crate::statement::Continue,
) -> ControlFlow<Self::BreakTy> {
if !self.iteration {
return ControlFlow::Break(CheckLabelsError::IllegalContinueStatement);
}
if let Some(label) = node.label()
&& !self.continue_iteration_labels.contains(&label)
{
return ControlFlow::Break(CheckLabelsError::UndefinedContinueTarget(label));
}
ControlFlow::Continue(())
}
fn visit_do_while_loop(
&mut self,
node: &'ast crate::statement::DoWhileLoop,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
let continue_iteration_labels = self.continue_iteration_labels.clone();
if let Some(continue_labels) = &continue_labels {
self.continue_iteration_labels.extend(continue_labels);
}
let iteration = self.iteration;
self.iteration = true;
self.visit_statement(node.body())?;
self.continue_iteration_labels = continue_iteration_labels;
self.continue_labels = continue_labels;
self.iteration = iteration;
ControlFlow::Continue(())
}
fn visit_while_loop(
&mut self,
node: &'ast crate::statement::WhileLoop,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
let continue_iteration_labels = self.continue_iteration_labels.clone();
if let Some(continue_labels) = &continue_labels {
self.continue_iteration_labels.extend(continue_labels);
}
let iteration = self.iteration;
self.iteration = true;
self.visit_statement(node.body())?;
self.continue_iteration_labels = continue_iteration_labels;
self.continue_labels = continue_labels;
self.iteration = iteration;
ControlFlow::Continue(())
}
fn visit_for_loop(
&mut self,
node: &'ast crate::statement::ForLoop,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
let continue_iteration_labels = self.continue_iteration_labels.clone();
if let Some(continue_labels) = &continue_labels {
self.continue_iteration_labels.extend(continue_labels);
}
let iteration = self.iteration;
self.iteration = true;
self.visit_statement(node.body())?;
self.continue_iteration_labels = continue_iteration_labels;
self.continue_labels = continue_labels;
self.iteration = iteration;
ControlFlow::Continue(())
}
fn visit_for_in_loop(
&mut self,
node: &'ast crate::statement::ForInLoop,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
let continue_iteration_labels = self.continue_iteration_labels.clone();
if let Some(continue_labels) = &continue_labels {
self.continue_iteration_labels.extend(continue_labels);
}
let iteration = self.iteration;
self.iteration = true;
self.visit_statement(node.body())?;
self.continue_iteration_labels = continue_iteration_labels;
self.continue_labels = continue_labels;
self.iteration = iteration;
ControlFlow::Continue(())
}
fn visit_for_of_loop(
&mut self,
node: &'ast crate::statement::ForOfLoop,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
let continue_iteration_labels = self.continue_iteration_labels.clone();
if let Some(continue_labels) = &continue_labels {
self.continue_iteration_labels.extend(continue_labels);
}
let iteration = self.iteration;
self.iteration = true;
self.visit_statement(node.body())?;
self.continue_iteration_labels = continue_iteration_labels;
self.continue_labels = continue_labels;
self.iteration = iteration;
ControlFlow::Continue(())
}
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
if let StatementListItem::Statement(stmt) = node {
self.visit_statement(stmt)?;
}
self.continue_labels = continue_labels;
ControlFlow::Continue(())
}
fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
self.visit_statement(node.body())?;
if let Some(stmt) = node.else_node() {
self.visit_statement(stmt)?;
}
self.continue_labels = continue_labels;
ControlFlow::Continue(())
}
fn visit_switch(
&mut self,
node: &'ast crate::statement::Switch,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
let switch = self.switch;
self.switch = true;
for case in node.cases() {
self.visit_statement_list(case.body())?;
}
if let Some(default) = node.default() {
self.visit_statement_list(default)?;
}
self.continue_labels = continue_labels;
self.switch = switch;
ControlFlow::Continue(())
}
fn visit_labelled(
&mut self,
node: &'ast crate::statement::Labelled,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.clone();
if let Some(continue_labels) = &mut self.continue_labels {
continue_labels.insert(node.label());
} else {
let mut continue_labels = FxHashSet::default();
continue_labels.insert(node.label());
self.continue_labels = Some(continue_labels);
}
if !self.labels.insert(node.label()) {
return ControlFlow::Break(CheckLabelsError::DuplicateLabel(node.label()));
}
self.visit_labelled_item(node.item())?;
self.labels.remove(&node.label());
self.continue_labels = continue_labels;
ControlFlow::Continue(())
}
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Statement(stmt) => self.visit_statement(stmt),
LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),
}
}
fn visit_try(&mut self, node: &'ast crate::statement::Try) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
self.visit_block(node.block())?;
if let Some(catch) = node.catch() {
self.visit_block(catch.block())?;
}
if let Some(finally) = node.finally() {
self.visit_block(finally.block())?;
}
self.continue_labels = continue_labels;
ControlFlow::Continue(())
}
fn visit_module_item_list(
&mut self,
node: &'ast crate::ModuleItemList,
) -> ControlFlow<Self::BreakTy> {
let continue_labels = self.continue_labels.take();
for item in node.items() {
self.visit_module_item(item)?;
}
self.continue_labels = continue_labels;
ControlFlow::Continue(())
}
fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
match node {
ModuleItem::ImportDeclaration(_) | ModuleItem::ExportDeclaration(_) => {
ControlFlow::Continue(())
}
ModuleItem::StatementListItem(node) => self.visit_statement_list_item(node),
}
}
}
let mut visitor = CheckLabelsResolver {
labels: FxHashSet::default(),
continue_iteration_labels: FxHashSet::default(),
continue_labels: None,
iteration: false,
switch: false,
};
if let ControlFlow::Break(error) = node.visit_with(&mut visitor) {
Err(error)
} else {
Ok(())
}
}
#[must_use]
pub fn contains_invalid_object_literal<N>(node: &N) -> bool
where
N: VisitWith,
{
#[derive(Debug, Clone)]
struct ContainsInvalidObjectLiteral {}
impl<'ast> Visitor<'ast> for ContainsInvalidObjectLiteral {
type BreakTy = ();
fn visit_object_literal(
&mut self,
node: &'ast crate::expression::literal::ObjectLiteral,
) -> ControlFlow<Self::BreakTy> {
for pd in node.properties() {
if let PropertyDefinition::CoverInitializedName(..) = pd {
return ControlFlow::Break(());
}
self.visit_property_definition(pd)?;
}
ControlFlow::Continue(())
}
}
let mut visitor = ContainsInvalidObjectLiteral {};
node.visit_with(&mut visitor).is_break()
}
#[derive(Copy, Clone, Debug)]
pub enum LexicallyScopedDeclaration<'a> {
LexicalDeclaration(&'a LexicalDeclaration),
FunctionDeclaration(&'a FunctionDeclaration),
GeneratorDeclaration(&'a GeneratorDeclaration),
AsyncFunctionDeclaration(&'a AsyncFunctionDeclaration),
AsyncGeneratorDeclaration(&'a AsyncGeneratorDeclaration),
ClassDeclaration(&'a ClassDeclaration),
AssignmentExpression(&'a Expression),
}
impl LexicallyScopedDeclaration<'_> {
#[must_use]
pub fn bound_names(&self) -> Vec<Sym> {
match *self {
Self::LexicalDeclaration(v) => bound_names(v),
Self::FunctionDeclaration(f) => bound_names(f),
Self::GeneratorDeclaration(g) => bound_names(g),
Self::AsyncFunctionDeclaration(f) => bound_names(f),
Self::AsyncGeneratorDeclaration(g) => bound_names(g),
Self::ClassDeclaration(cl) => bound_names(cl),
Self::AssignmentExpression(expr) => bound_names(expr),
}
}
}
impl<'ast> From<&'ast Declaration> for LexicallyScopedDeclaration<'ast> {
fn from(value: &'ast Declaration) -> LexicallyScopedDeclaration<'ast> {
match value {
Declaration::FunctionDeclaration(f) => Self::FunctionDeclaration(f),
Declaration::GeneratorDeclaration(g) => Self::GeneratorDeclaration(g),
Declaration::AsyncFunctionDeclaration(af) => Self::AsyncFunctionDeclaration(af),
Declaration::AsyncGeneratorDeclaration(ag) => Self::AsyncGeneratorDeclaration(ag),
Declaration::ClassDeclaration(c) => Self::ClassDeclaration(c),
Declaration::Lexical(lex) => Self::LexicalDeclaration(lex),
}
}
}
#[must_use]
pub fn lexically_scoped_declarations<'a, N>(node: &'a N) -> Vec<LexicallyScopedDeclaration<'a>>
where
&'a N: Into<NodeRef<'a>>,
{
let mut declarations = Vec::new();
let _ = LexicallyScopedDeclarationsVisitor(&mut declarations).visit(node.into());
declarations
}
#[derive(Debug)]
struct LexicallyScopedDeclarationsVisitor<'a, 'ast>(&'a mut Vec<LexicallyScopedDeclaration<'ast>>);
impl<'ast> Visitor<'ast> for LexicallyScopedDeclarationsVisitor<'_, 'ast> {
type BreakTy = Infallible;
fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {
TopLevelLexicallyScopedDeclarationsVisitor(self.0).visit_statement_list(node.statements())
}
fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {
TopLevelLexicallyScopedDeclarationsVisitor(self.0)
.visit_statement_list(node.statement_list())
}
fn visit_export_declaration(
&mut self,
node: &'ast ExportDeclaration,
) -> ControlFlow<Self::BreakTy> {
let decl = match node {
ExportDeclaration::ReExport { .. }
| ExportDeclaration::List(_)
| ExportDeclaration::VarStatement(_) => {
return ControlFlow::Continue(());
}
ExportDeclaration::Declaration(decl) => {
decl.into()
}
ExportDeclaration::DefaultFunctionDeclaration(f) => {
LexicallyScopedDeclaration::FunctionDeclaration(f)
}
ExportDeclaration::DefaultGeneratorDeclaration(g) => {
LexicallyScopedDeclaration::GeneratorDeclaration(g)
}
ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {
LexicallyScopedDeclaration::AsyncFunctionDeclaration(af)
}
ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {
LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag)
}
ExportDeclaration::DefaultClassDeclaration(c) => {
LexicallyScopedDeclaration::ClassDeclaration(c)
}
ExportDeclaration::DefaultAssignmentExpression(expr) => {
LexicallyScopedDeclaration::AssignmentExpression(expr)
}
};
self.0.push(decl);
ControlFlow::Continue(())
}
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
match node {
StatementListItem::Statement(statement) => {
if let Statement::Labelled(labelled) = statement.as_ref() {
self.visit_labelled(labelled)
} else {
ControlFlow::Continue(())
}
}
StatementListItem::Declaration(declaration) => {
self.0.push(declaration.as_ref().into());
ControlFlow::Continue(())
}
}
}
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::FunctionDeclaration(f) => {
self.0
.push(LexicallyScopedDeclaration::FunctionDeclaration(f));
}
LabelledItem::Statement(_) => {
}
}
ControlFlow::Continue(())
}
fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
match node {
ModuleItem::StatementListItem(item) => self.visit_statement_list_item(item),
ModuleItem::ExportDeclaration(export) => self.visit_export_declaration(export),
ModuleItem::ImportDeclaration(_) => {
ControlFlow::Continue(())
}
}
}
}
#[derive(Debug)]
struct TopLevelLexicallyScopedDeclarationsVisitor<'a, 'ast>(
&'a mut Vec<LexicallyScopedDeclaration<'ast>>,
);
impl<'ast> Visitor<'ast> for TopLevelLexicallyScopedDeclarationsVisitor<'_, 'ast> {
type BreakTy = Infallible;
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
match node {
StatementListItem::Declaration(d) => match d.as_ref() {
Declaration::FunctionDeclaration(_)
| Declaration::GeneratorDeclaration(_)
| Declaration::AsyncFunctionDeclaration(_)
| Declaration::AsyncGeneratorDeclaration(_) => {
}
Declaration::ClassDeclaration(cl) => {
self.0
.push(LexicallyScopedDeclaration::ClassDeclaration(cl));
}
Declaration::Lexical(lex) => {
self.0
.push(LexicallyScopedDeclaration::LexicalDeclaration(lex));
}
},
StatementListItem::Statement(_) => {
}
}
ControlFlow::Continue(())
}
}
#[derive(Clone, Debug)]
pub enum VarScopedDeclaration {
VariableDeclaration(Variable),
FunctionDeclaration(FunctionDeclaration),
GeneratorDeclaration(GeneratorDeclaration),
AsyncFunctionDeclaration(AsyncFunctionDeclaration),
AsyncGeneratorDeclaration(AsyncGeneratorDeclaration),
}
impl VarScopedDeclaration {
#[must_use]
pub fn bound_names(&self) -> Vec<Sym> {
match self {
Self::VariableDeclaration(v) => bound_names(v),
Self::FunctionDeclaration(f) => bound_names(f),
Self::GeneratorDeclaration(g) => bound_names(g),
Self::AsyncFunctionDeclaration(f) => bound_names(f),
Self::AsyncGeneratorDeclaration(g) => bound_names(g),
}
}
#[must_use]
pub fn linear_span(&self) -> Option<LinearSpan> {
match self {
VarScopedDeclaration::FunctionDeclaration(f) => Some(f.linear_span()),
VarScopedDeclaration::GeneratorDeclaration(f) => Some(f.linear_span()),
VarScopedDeclaration::AsyncFunctionDeclaration(f) => Some(f.linear_span()),
VarScopedDeclaration::AsyncGeneratorDeclaration(f) => Some(f.linear_span()),
VarScopedDeclaration::VariableDeclaration(_) => None,
}
}
}
#[must_use]
pub fn var_scoped_declarations<'a, N>(node: &'a N) -> Vec<VarScopedDeclaration>
where
&'a N: Into<NodeRef<'a>>,
{
let mut declarations = Vec::new();
let _ = VarScopedDeclarationsVisitor(&mut declarations).visit(node.into());
declarations
}
#[derive(Debug)]
struct VarScopedDeclarationsVisitor<'a>(&'a mut Vec<VarScopedDeclaration>);
impl<'ast> Visitor<'ast> for VarScopedDeclarationsVisitor<'_> {
type BreakTy = Infallible;
fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {
TopLevelVarScopedDeclarationsVisitor(self.0).visit_statement_list(node.statements())
}
fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {
TopLevelVarScopedDeclarationsVisitor(self.0).visit_statement_list(node.statement_list())
}
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
match node {
Statement::Block(s) => self.visit(s),
Statement::Var(s) => self.visit(s),
Statement::If(s) => self.visit(s),
Statement::DoWhileLoop(s) => self.visit(s),
Statement::WhileLoop(s) => self.visit(s),
Statement::ForLoop(s) => self.visit(s),
Statement::ForInLoop(s) => self.visit(s),
Statement::ForOfLoop(s) => self.visit(s),
Statement::Switch(s) => self.visit(s),
Statement::Labelled(s) => self.visit(s),
Statement::Try(s) => self.visit(s),
Statement::With(s) => self.visit(s),
Statement::Empty
| Statement::Expression(_)
| Statement::Continue(_)
| Statement::Break(_)
| Statement::Return(_)
| Statement::Throw(_) => ControlFlow::Continue(()),
}
}
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
match node {
StatementListItem::Declaration(_) => ControlFlow::Continue(()),
StatementListItem::Statement(s) => self.visit(s.as_ref()),
}
}
fn visit_var_declaration(&mut self, node: &'ast VarDeclaration) -> ControlFlow<Self::BreakTy> {
for var in node.0.as_ref() {
self.0
.push(VarScopedDeclaration::VariableDeclaration(var.clone()));
}
ControlFlow::Continue(())
}
fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
if let Some(else_node) = node.else_node() {
self.visit(else_node)?;
}
ControlFlow::Continue(())
}
fn visit_do_while_loop(
&mut self,
node: &'ast crate::statement::DoWhileLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
ControlFlow::Continue(())
}
fn visit_while_loop(
&mut self,
node: &'ast crate::statement::WhileLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
ControlFlow::Continue(())
}
fn visit_for_loop(
&mut self,
node: &'ast crate::statement::ForLoop,
) -> ControlFlow<Self::BreakTy> {
if let Some(ForLoopInitializer::Var(v)) = node.init() {
self.visit(v)?;
}
self.visit(node.body())?;
ControlFlow::Continue(())
}
fn visit_for_in_loop(
&mut self,
node: &'ast crate::statement::ForInLoop,
) -> ControlFlow<Self::BreakTy> {
if let IterableLoopInitializer::Var(var) = node.initializer() {
self.0
.push(VarScopedDeclaration::VariableDeclaration(var.clone()));
}
self.visit(node.body())?;
ControlFlow::Continue(())
}
fn visit_for_of_loop(
&mut self,
node: &'ast crate::statement::ForOfLoop,
) -> ControlFlow<Self::BreakTy> {
if let IterableLoopInitializer::Var(var) = node.initializer() {
self.0
.push(VarScopedDeclaration::VariableDeclaration(var.clone()));
}
self.visit(node.body())?;
ControlFlow::Continue(())
}
fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {
self.visit(node.statement())?;
ControlFlow::Continue(())
}
fn visit_switch(&mut self, node: &'ast crate::statement::Switch) -> ControlFlow<Self::BreakTy> {
for case in node.cases() {
self.visit(case)?;
}
if let Some(default) = node.default() {
self.visit(default)?;
}
ControlFlow::Continue(())
}
fn visit_case(&mut self, node: &'ast crate::statement::Case) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
ControlFlow::Continue(())
}
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Statement(s) => self.visit(s),
LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),
}
}
fn visit_catch(&mut self, node: &'ast crate::statement::Catch) -> ControlFlow<Self::BreakTy> {
self.visit(node.block())?;
ControlFlow::Continue(())
}
fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
match node {
ModuleItem::ExportDeclaration(decl) => {
if let ExportDeclaration::VarStatement(var) = decl.as_ref() {
self.visit_var_declaration(var)?;
}
}
ModuleItem::StatementListItem(item) => {
self.visit_statement_list_item(item)?;
}
ModuleItem::ImportDeclaration(_) => {
}
}
ControlFlow::Continue(())
}
}
#[derive(Debug)]
struct TopLevelVarScopedDeclarationsVisitor<'a>(&'a mut Vec<VarScopedDeclaration>);
impl<'ast> Visitor<'ast> for TopLevelVarScopedDeclarationsVisitor<'_> {
type BreakTy = Infallible;
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
match node {
StatementListItem::Declaration(d) => {
match d.as_ref() {
Declaration::FunctionDeclaration(f) => {
self.0
.push(VarScopedDeclaration::FunctionDeclaration(f.clone()));
}
Declaration::GeneratorDeclaration(f) => {
self.0
.push(VarScopedDeclaration::GeneratorDeclaration(f.clone()));
}
Declaration::AsyncFunctionDeclaration(f) => {
self.0
.push(VarScopedDeclaration::AsyncFunctionDeclaration(f.clone()));
}
Declaration::AsyncGeneratorDeclaration(f) => {
self.0
.push(VarScopedDeclaration::AsyncGeneratorDeclaration(f.clone()));
}
_ => {}
}
ControlFlow::Continue(())
}
StatementListItem::Statement(statement) => {
if let Statement::Labelled(labelled) = statement.as_ref() {
self.visit(labelled)
} else {
VarScopedDeclarationsVisitor(self.0).visit(statement.as_ref())
}
}
}
}
fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {
match node {
LabelledItem::Statement(Statement::Labelled(s)) => self.visit(s),
LabelledItem::Statement(s) => {
VarScopedDeclarationsVisitor(self.0).visit(s)?;
ControlFlow::Continue(())
}
LabelledItem::FunctionDeclaration(f) => {
self.0
.push(VarScopedDeclaration::FunctionDeclaration(f.clone()));
ControlFlow::Continue(())
}
}
}
}
#[must_use]
pub fn annex_b_function_declarations_names<'a, N>(node: &'a N) -> Vec<Sym>
where
&'a N: Into<NodeRef<'a>>,
{
let mut declarations = Vec::new();
let _ = AnnexBFunctionDeclarationNamesVisitor(&mut declarations).visit(node.into());
declarations
}
#[derive(Debug)]
struct AnnexBFunctionDeclarationNamesVisitor<'a>(&'a mut Vec<Sym>);
impl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {
type BreakTy = Infallible;
fn visit_statement_list_item(
&mut self,
node: &'ast StatementListItem,
) -> ControlFlow<Self::BreakTy> {
match node {
StatementListItem::Statement(node) => self.visit(node.as_ref()),
StatementListItem::Declaration(_) => ControlFlow::Continue(()),
}
}
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
match node {
Statement::Block(node) => self.visit(node),
Statement::If(node) => self.visit(node),
Statement::DoWhileLoop(node) => self.visit(node),
Statement::WhileLoop(node) => self.visit(node),
Statement::ForLoop(node) => self.visit(node),
Statement::ForInLoop(node) => self.visit(node),
Statement::ForOfLoop(node) => self.visit(node),
Statement::Switch(node) => self.visit(node),
Statement::Labelled(node) => self.visit(node),
Statement::Try(node) => self.visit(node),
Statement::With(node) => self.visit(node),
_ => ControlFlow::Continue(()),
}
}
fn visit_block(&mut self, node: &'ast crate::statement::Block) -> ControlFlow<Self::BreakTy> {
self.visit(node.statement_list())?;
for statement in node.statement_list().statements() {
if let StatementListItem::Declaration(declaration) = statement
&& let Declaration::FunctionDeclaration(function) = &**declaration
{
let name = function.name();
self.0.push(name.sym());
}
}
let lexically_declared_names = lexically_declared_names_legacy(node.statement_list());
self.0
.retain(|name| !lexically_declared_names.contains(&(*name, false)));
ControlFlow::Continue(())
}
fn visit_switch(&mut self, node: &'ast crate::statement::Switch) -> ControlFlow<Self::BreakTy> {
for case in node.cases() {
self.visit(case)?;
for statement in case.body().statements() {
if let StatementListItem::Declaration(declaration) = statement
&& let Declaration::FunctionDeclaration(function) = &**declaration
{
let name = function.name();
self.0.push(name.sym());
}
}
}
if let Some(default) = node.default() {
self.visit(default)?;
for statement in default.statements() {
if let StatementListItem::Declaration(declaration) = statement
&& let Declaration::FunctionDeclaration(function) = declaration.as_ref()
{
let name = function.name();
self.0.push(name.sym());
}
}
}
let lexically_declared_names = lexically_declared_names_legacy(node);
self.0
.retain(|name| !lexically_declared_names.contains(&(*name, false)));
ControlFlow::Continue(())
}
fn visit_try(&mut self, node: &'ast crate::statement::Try) -> ControlFlow<Self::BreakTy> {
self.visit(node.block())?;
if let Some(catch) = node.catch() {
self.visit(catch.block())?;
if let Some(Binding::Pattern(pattern)) = catch.parameter() {
let bound_names = bound_names(pattern);
self.0.retain(|name| !bound_names.contains(name));
}
}
if let Some(finally) = node.finally() {
self.visit(finally.block())?;
}
ControlFlow::Continue(())
}
fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {
if let Some(node) = node.else_node() {
self.visit(node)?;
}
self.visit(node.body())
}
fn visit_do_while_loop(
&mut self,
node: &'ast crate::statement::DoWhileLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())
}
fn visit_while_loop(
&mut self,
node: &'ast crate::statement::WhileLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())
}
fn visit_for_loop(
&mut self,
node: &'ast crate::statement::ForLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
if let Some(ForLoopInitializer::Lexical(node)) = node.init() {
let bound_names = bound_names(&node.declaration);
self.0.retain(|name| !bound_names.contains(name));
}
ControlFlow::Continue(())
}
fn visit_for_in_loop(
&mut self,
node: &'ast crate::statement::ForInLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
if let IterableLoopInitializer::Let(node) = node.initializer() {
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
if let IterableLoopInitializer::Const(node) = node.initializer() {
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
ControlFlow::Continue(())
}
fn visit_for_of_loop(
&mut self,
node: &'ast crate::statement::ForOfLoop,
) -> ControlFlow<Self::BreakTy> {
self.visit(node.body())?;
if let IterableLoopInitializer::Let(node) = node.initializer() {
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
if let IterableLoopInitializer::Const(node) = node.initializer() {
let bound_names = bound_names(node);
self.0.retain(|name| !bound_names.contains(name));
}
ControlFlow::Continue(())
}
fn visit_labelled(
&mut self,
node: &'ast crate::statement::Labelled,
) -> ControlFlow<Self::BreakTy> {
if let LabelledItem::Statement(node) = node.item() {
self.visit(node)?;
}
ControlFlow::Continue(())
}
fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {
self.visit(node.statement())
}
}
#[must_use]
pub fn returns_value<'a, N>(node: &'a N) -> bool
where
&'a N: Into<NodeRef<'a>>,
{
ReturnsValueVisitor.visit(node.into()).is_break()
}
#[derive(Debug)]
struct ReturnsValueVisitor;
impl<'ast> Visitor<'ast> for ReturnsValueVisitor {
type BreakTy = ();
fn visit_block(&mut self, node: &'ast crate::statement::Block) -> ControlFlow<Self::BreakTy> {
for statement in node.statement_list().statements() {
match statement {
StatementListItem::Declaration(_) => {}
StatementListItem::Statement(node) => self.visit(node.as_ref())?,
}
}
ControlFlow::Continue(())
}
fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {
match node {
Statement::Empty | Statement::Var(_) => {}
Statement::Block(node) => self.visit(node)?,
Statement::Labelled(node) => self.visit(node)?,
_ => return ControlFlow::Break(()),
}
ControlFlow::Continue(())
}
fn visit_case(&mut self, node: &'ast crate::statement::Case) -> ControlFlow<Self::BreakTy> {
for statement in node.body().statements() {
match statement {
StatementListItem::Declaration(_) => {}
StatementListItem::Statement(node) => self.visit(node.as_ref())?,
}
}
ControlFlow::Continue(())
}
fn visit_labelled(
&mut self,
node: &'ast crate::statement::Labelled,
) -> ControlFlow<Self::BreakTy> {
match node.item() {
LabelledItem::Statement(node) => self.visit(node)?,
LabelledItem::FunctionDeclaration(_) => {}
}
ControlFlow::Continue(())
}
}