in unsupported/juno/crates/juno/src/sema/resolver.rs [1142:1388]
fn call(&mut self, lock: &'gc GCLock, node: &'gc Node<'gc>, path: Option<Path<'gc>>) {
match node {
Node::Program(_) => {
panic!("visited unexpected {}", node.name())
}
Node::FunctionDeclaration(..) => {
// Collect hoisted function.
self.sem
.scope_mut(self.current_scope.unwrap())
.hoisted_functions
.push(NodeRc::from_node(lock, node));
self.visit_function_like(lock, node);
}
Node::FunctionExpression(fe) => self.visit_function_expression(lock, fe, node),
Node::Identifier(ident) => self.visit_identifier(lock, ident, node, path.unwrap()),
Node::BlockStatement(_) => self.visit_block_statement(lock, node, path.unwrap()),
Node::SwitchStatement(switch) => {
// Visit the discriminant before creating a new scope.
switch.discriminant.visit(
lock,
self,
Some(Path::new(node, NodeField::discriminant)),
);
self.with_new_label(lock, None, node, |pself| {
pself.in_new_scope(lock, node, |pself| {
if let Some(decls) =
pself.function_context().decls.scope_decls_for_node(node)
{
pself.process_declarations(lock, &decls);
}
for case in &switch.cases {
case.visit(lock, pself, Some(Path::new(node, NodeField::cases)));
}
});
});
}
Node::ForStatement(_) | Node::ForInStatement(_) | Node::ForOfStatement(_) => {
self.with_new_label(lock, None, node, |pself| {
// Only create a lexical scope if there are declarations in it.
if let Some(decls) = pself.function_context().decls.scope_decls_for_node(node) {
pself.in_new_scope(lock, node, |pself| {
pself.process_declarations(lock, decls.as_slice());
node.visit_children(lock, pself);
});
} else {
node.visit_children(lock, pself);
}
});
}
Node::WhileStatement(_) | Node::DoWhileStatement(_) => {
self.with_new_label(lock, None, node, |pself| {
node.visit_children(lock, pself);
});
}
Node::LabeledStatement(labeled) => {
self.with_new_label(lock, Some(labeled.label), node, |pself| {
node.visit_children(lock, pself);
});
}
Node::BreakStatement(ast::BreakStatement {
label: Some(label), ..
}) => {
let name = node_cast!(Node::Identifier, label).name;
if self.function_context().label_table.get(&name).is_none() {
lock.sm().error(
*label.range(),
format!("label '{}' is not defined", lock.str(name)),
);
}
}
Node::BreakStatement(ast::BreakStatement { label: None, .. }) => {
if self.current_loop_or_switch.is_none() {
lock.sm()
.error(*node.range(), "'break' not within a loop or switch");
}
}
Node::ContinueStatement(ast::ContinueStatement {
label: Some(label_node),
..
}) => {
let name = node_cast!(Node::Identifier, label_node).name;
match self.function_context().label_table.get(&name) {
Some(label) => {
if matches!(label.target_statement, Node::LabeledStatement(_)) {
lock.sm().error(
*label_node.range(),
format!(
"'continue' label '{}' is not a loop label",
lock.str(name)
),
);
}
}
None => {
lock.sm().error(
*label_node.range(),
format!("label '{}' is not defined", lock.str(name)),
);
}
}
}
Node::ContinueStatement(ast::ContinueStatement { label: None, .. }) => {
if self.current_loop.is_none() {
lock.sm()
.error(*node.range(), "'continue' not within a loop or switch");
}
}
Node::CatchClause(ast::CatchClause { param, body, .. }) => {
self.in_new_scope(lock, node, |pself| {
if let Some(id_node @ Node::Identifier(_)) = param {
// For compatibility with ES5, we need to treat a single catch variable
// specially, see: B.3.5 VariableStatements in Catch Blocks
// https://www.ecma-international.org/ecma-262/10.0/index.html#sec-variablestatements-in-catch-blocks
pself.validate_and_declare_identifier(lock, DeclKind::ES5Catch, id_node);
} else {
let mut idents = SmallVec::<[&Node; 4]>::new();
Self::extract_declared_idents_from_id(lock, *param, &mut idents);
for id_node in idents {
pself.validate_and_declare_identifier(lock, DeclKind::Let, id_node);
}
}
// Process body's declarations, skip visiting it, visit its children.
pself.process_collected_declarations(lock, body);
body.visit_children(lock, pself);
});
}
Node::MetaProperty(ast::MetaProperty {
meta: Node::Identifier(meta),
property: Node::Identifier(prop),
..
}) => {
// Validate "new.target"
if meta.name == self.kw.ident_new
&& prop.name == self.kw.ident_target
&& self.function_context().func_id.is_global()
{
// ES9.0 15.1.1:
// It is a Syntax Error if StatementList Contains NewTarget unless the
// source code containing NewTarget is eval code that is being processed
// by a direct eval.
lock.sm()
.error(*node.range(), "'new.target' outside of a function");
}
}
Node::ImportDeclaration(ast::ImportDeclaration {
source: Node::StringLiteral(ast::StringLiteral { value, .. }),
..
}) => {
node.visit_children(lock, self);
if let ResolverMode::Module {
dependency_resolver,
} = self.mode
{
// Resolve `import`.
let target = String::from_utf16_lossy(&value.str);
match dependency_resolver.resolve_dependency(
lock,
self.file_id,
&target,
DependencyKind::Import,
) {
Some(file_id) => {
self.sem.add_require(NodeRc::from_node(lock, node), file_id);
}
None => {
lock.sm().warning(
*node.range(),
format!("Unable to resolve import for {}", target),
);
}
}
}
}
Node::ClassDeclaration(_) | Node::ClassExpression(_) => {
let old_strict = self.function_strict_mode();
*self.function_strict_mode_mut() = true;
node.visit_children(lock, self);
*self.function_strict_mode_mut() = old_strict;
}
Node::CallExpression(call @ ast::CallExpression { arguments, .. }) => {
// Check for a direct call to local `eval()`.
if let Node::Identifier(identifier) = call.callee {
if identifier.name == self.kw.ident_eval {
let is_eval = match self.binding_table.get(&identifier.name) {
None => true,
Some(name) => {
let decl = self.sem.decl(name.decl);
decl.scope.is_global() && decl.kind.is_var_like()
}
};
if is_eval {
self.register_local_eval();
}
}
}
node.visit_children(lock, self);
if let ResolverMode::Module {
dependency_resolver,
} = self.mode
{
if self.is_require(lock, call) {
// Resolve `require()` call.
if let Node::StringLiteral(ast::StringLiteral { value, .. }) = arguments[0]
{
let target = String::from_utf16_lossy(&value.str);
match dependency_resolver.resolve_dependency(
lock,
self.file_id,
&target,
DependencyKind::Require,
) {
Some(file_id) => {
self.sem.add_require(NodeRc::from_node(lock, node), file_id);
}
None => {
lock.sm().warning(
*node.range(),
format!("Unable to resolve require for {}", target),
);
}
}
}
}
}
}
_ => {
node.visit_children(lock, self);
}
}
}