From 1e683f9ca5a254357b5f9107c3075394d419a6e1 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:54:18 +0000 Subject: [PATCH] fix(traverse): `ChildScopeCollector` visit all scopes (#10292) Fixes #9702. Fix `ChildScopeCollector` so it doesn't miss out scopes in some cases. Previously when a struct has its own scope, the visitor does not visit any of the struct's fields. But this is incorrect where the type's scope does not cover all of its fields (the scope starts late / ends early). If the fields outside of the scope may themselves have scopes, then those scopes are direct children of the starting point. These types are: * `Class` (`decorators` is outside `Class`'s scope). * `SwitchStatement` (`discriminant` is outside `SwitchStatement`'s scope). * `TSConditionalType` (`check_type` and `false_type` are outside `TSConditionalType`'s scope). Additionally, make the visitor more efficient by not bothering to visit any struct fields / enum variants which don't contain a scope anywhere in their dependencies. --- .../src/generated/scopes_collector.rs | 1925 ++++++++++++++++- .../src/generators/scopes_collector.rs | 532 ++++- tasks/ast_tools/src/generators/visit.rs | 46 +- tasks/ast_tools/src/main.rs | 2 +- .../ast_tools/src/schema/extensions/visit.rs | 4 + 5 files changed, 2421 insertions(+), 88 deletions(-) diff --git a/crates/oxc_traverse/src/generated/scopes_collector.rs b/crates/oxc_traverse/src/generated/scopes_collector.rs index 7316f709782ff..f80d50ac8b74b 100644 --- a/crates/oxc_traverse/src/generated/scopes_collector.rs +++ b/crates/oxc_traverse/src/generated/scopes_collector.rs @@ -1,6 +1,14 @@ // Auto-generated code, DO NOT EDIT DIRECTLY! // To edit this generated file you have to edit `tasks/ast_tools/src/generators/scopes_collector.rs` +#![expect( + unused_variables, + clippy::semicolon_if_nothing_returned, + clippy::match_wildcard_for_single_variants, + clippy::match_same_arms, + clippy::single_match_else +)] + use std::cell::Cell; use oxc_ast::ast::*; @@ -8,7 +16,8 @@ use oxc_ast_visit::Visit; use oxc_syntax::scope::{ScopeFlags, ScopeId}; /// Visitor that locates all child scopes. -/// NB: Child scopes only, not grandchild scopes. +/// +/// Note: Direct child scopes only, not grandchild scopes. /// Does not do full traversal - stops each time it hits a node with a scope. pub struct ChildScopeCollector { pub(crate) scope_ids: Vec, @@ -30,101 +39,1969 @@ impl<'a> Visit<'a> for ChildScopeCollector { self.add_scope(&it.scope_id); } + fn visit_expression(&mut self, it: &Expression<'a>) { + match it { + Expression::TemplateLiteral(it) => self.visit_template_literal(it), + Expression::ArrayExpression(it) => self.visit_array_expression(it), + Expression::ArrowFunctionExpression(it) => self.visit_arrow_function_expression(it), + Expression::AssignmentExpression(it) => self.visit_assignment_expression(it), + Expression::AwaitExpression(it) => self.visit_await_expression(it), + Expression::BinaryExpression(it) => self.visit_binary_expression(it), + Expression::CallExpression(it) => self.visit_call_expression(it), + Expression::ChainExpression(it) => self.visit_chain_expression(it), + Expression::ClassExpression(it) => self.visit_class(it), + Expression::ConditionalExpression(it) => self.visit_conditional_expression(it), + Expression::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + Expression::ImportExpression(it) => self.visit_import_expression(it), + Expression::LogicalExpression(it) => self.visit_logical_expression(it), + Expression::NewExpression(it) => self.visit_new_expression(it), + Expression::ObjectExpression(it) => self.visit_object_expression(it), + Expression::ParenthesizedExpression(it) => self.visit_parenthesized_expression(it), + Expression::SequenceExpression(it) => self.visit_sequence_expression(it), + Expression::TaggedTemplateExpression(it) => self.visit_tagged_template_expression(it), + Expression::UnaryExpression(it) => self.visit_unary_expression(it), + Expression::UpdateExpression(it) => self.visit_update_expression(it), + Expression::YieldExpression(it) => self.visit_yield_expression(it), + Expression::PrivateInExpression(it) => self.visit_private_in_expression(it), + Expression::JSXElement(it) => self.visit_jsx_element(it), + Expression::JSXFragment(it) => self.visit_jsx_fragment(it), + Expression::TSAsExpression(it) => self.visit_ts_as_expression(it), + Expression::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + Expression::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + Expression::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + Expression::TSInstantiationExpression(it) => self.visit_ts_instantiation_expression(it), + Expression::V8IntrinsicExpression(it) => self.visit_v_8_intrinsic_expression(it), + Expression::ComputedMemberExpression(it) => self.visit_computed_member_expression(it), + Expression::StaticMemberExpression(it) => self.visit_static_member_expression(it), + Expression::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + _ => { + // Remaining variants do not contain scopes: + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + + #[inline(always)] + fn visit_identifier_name(&mut self, it: &IdentifierName<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_identifier_reference(&mut self, it: &IdentifierReference<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_binding_identifier(&mut self, it: &BindingIdentifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_label_identifier(&mut self, it: &LabelIdentifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_this_expression(&mut self, it: &ThisExpression) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_array_expression(&mut self, it: &ArrayExpression<'a>) { + self.visit_array_expression_elements(&it.elements); + } + + fn visit_array_expression_element(&mut self, it: &ArrayExpressionElement<'a>) { + match it { + ArrayExpressionElement::SpreadElement(it) => self.visit_spread_element(it), + ArrayExpressionElement::TemplateLiteral(it) => self.visit_template_literal(it), + ArrayExpressionElement::ArrayExpression(it) => self.visit_array_expression(it), + ArrayExpressionElement::ArrowFunctionExpression(it) => { + self.visit_arrow_function_expression(it) + } + ArrayExpressionElement::AssignmentExpression(it) => { + self.visit_assignment_expression(it) + } + ArrayExpressionElement::AwaitExpression(it) => self.visit_await_expression(it), + ArrayExpressionElement::BinaryExpression(it) => self.visit_binary_expression(it), + ArrayExpressionElement::CallExpression(it) => self.visit_call_expression(it), + ArrayExpressionElement::ChainExpression(it) => self.visit_chain_expression(it), + ArrayExpressionElement::ClassExpression(it) => self.visit_class(it), + ArrayExpressionElement::ConditionalExpression(it) => { + self.visit_conditional_expression(it) + } + ArrayExpressionElement::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + ArrayExpressionElement::ImportExpression(it) => self.visit_import_expression(it), + ArrayExpressionElement::LogicalExpression(it) => self.visit_logical_expression(it), + ArrayExpressionElement::NewExpression(it) => self.visit_new_expression(it), + ArrayExpressionElement::ObjectExpression(it) => self.visit_object_expression(it), + ArrayExpressionElement::ParenthesizedExpression(it) => { + self.visit_parenthesized_expression(it) + } + ArrayExpressionElement::SequenceExpression(it) => self.visit_sequence_expression(it), + ArrayExpressionElement::TaggedTemplateExpression(it) => { + self.visit_tagged_template_expression(it) + } + ArrayExpressionElement::UnaryExpression(it) => self.visit_unary_expression(it), + ArrayExpressionElement::UpdateExpression(it) => self.visit_update_expression(it), + ArrayExpressionElement::YieldExpression(it) => self.visit_yield_expression(it), + ArrayExpressionElement::PrivateInExpression(it) => self.visit_private_in_expression(it), + ArrayExpressionElement::JSXElement(it) => self.visit_jsx_element(it), + ArrayExpressionElement::JSXFragment(it) => self.visit_jsx_fragment(it), + ArrayExpressionElement::TSAsExpression(it) => self.visit_ts_as_expression(it), + ArrayExpressionElement::TSSatisfiesExpression(it) => { + self.visit_ts_satisfies_expression(it) + } + ArrayExpressionElement::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + ArrayExpressionElement::TSNonNullExpression(it) => { + self.visit_ts_non_null_expression(it) + } + ArrayExpressionElement::TSInstantiationExpression(it) => { + self.visit_ts_instantiation_expression(it) + } + ArrayExpressionElement::V8IntrinsicExpression(it) => { + self.visit_v_8_intrinsic_expression(it) + } + ArrayExpressionElement::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + ArrayExpressionElement::StaticMemberExpression(it) => { + self.visit_static_member_expression(it) + } + ArrayExpressionElement::PrivateFieldExpression(it) => { + self.visit_private_field_expression(it) + } + _ => { + // Remaining variants do not contain scopes: + // `Elision` + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + + #[inline(always)] + fn visit_elision(&mut self, it: &Elision) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_object_expression(&mut self, it: &ObjectExpression<'a>) { + self.visit_object_property_kinds(&it.properties); + } + + #[inline] + fn visit_object_property(&mut self, it: &ObjectProperty<'a>) { + self.visit_property_key(&it.key); + self.visit_expression(&it.value); + } + + fn visit_property_key(&mut self, it: &PropertyKey<'a>) { + match it { + PropertyKey::TemplateLiteral(it) => self.visit_template_literal(it), + PropertyKey::ArrayExpression(it) => self.visit_array_expression(it), + PropertyKey::ArrowFunctionExpression(it) => self.visit_arrow_function_expression(it), + PropertyKey::AssignmentExpression(it) => self.visit_assignment_expression(it), + PropertyKey::AwaitExpression(it) => self.visit_await_expression(it), + PropertyKey::BinaryExpression(it) => self.visit_binary_expression(it), + PropertyKey::CallExpression(it) => self.visit_call_expression(it), + PropertyKey::ChainExpression(it) => self.visit_chain_expression(it), + PropertyKey::ClassExpression(it) => self.visit_class(it), + PropertyKey::ConditionalExpression(it) => self.visit_conditional_expression(it), + PropertyKey::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + PropertyKey::ImportExpression(it) => self.visit_import_expression(it), + PropertyKey::LogicalExpression(it) => self.visit_logical_expression(it), + PropertyKey::NewExpression(it) => self.visit_new_expression(it), + PropertyKey::ObjectExpression(it) => self.visit_object_expression(it), + PropertyKey::ParenthesizedExpression(it) => self.visit_parenthesized_expression(it), + PropertyKey::SequenceExpression(it) => self.visit_sequence_expression(it), + PropertyKey::TaggedTemplateExpression(it) => self.visit_tagged_template_expression(it), + PropertyKey::UnaryExpression(it) => self.visit_unary_expression(it), + PropertyKey::UpdateExpression(it) => self.visit_update_expression(it), + PropertyKey::YieldExpression(it) => self.visit_yield_expression(it), + PropertyKey::PrivateInExpression(it) => self.visit_private_in_expression(it), + PropertyKey::JSXElement(it) => self.visit_jsx_element(it), + PropertyKey::JSXFragment(it) => self.visit_jsx_fragment(it), + PropertyKey::TSAsExpression(it) => self.visit_ts_as_expression(it), + PropertyKey::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + PropertyKey::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + PropertyKey::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + PropertyKey::TSInstantiationExpression(it) => { + self.visit_ts_instantiation_expression(it) + } + PropertyKey::V8IntrinsicExpression(it) => self.visit_v_8_intrinsic_expression(it), + PropertyKey::ComputedMemberExpression(it) => self.visit_computed_member_expression(it), + PropertyKey::StaticMemberExpression(it) => self.visit_static_member_expression(it), + PropertyKey::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + _ => { + // Remaining variants do not contain scopes: + // `StaticIdentifier` + // `PrivateIdentifier` + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + + #[inline] + fn visit_template_literal(&mut self, it: &TemplateLiteral<'a>) { + self.visit_expressions(&it.expressions); + } + + #[inline] + fn visit_tagged_template_expression(&mut self, it: &TaggedTemplateExpression<'a>) { + self.visit_expression(&it.tag); + self.visit_template_literal(&it.quasi); + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline(always)] + fn visit_template_element(&mut self, it: &TemplateElement<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_computed_member_expression(&mut self, it: &ComputedMemberExpression<'a>) { + self.visit_expression(&it.object); + self.visit_expression(&it.expression); + } + + #[inline] + fn visit_static_member_expression(&mut self, it: &StaticMemberExpression<'a>) { + self.visit_expression(&it.object); + } + + #[inline] + fn visit_private_field_expression(&mut self, it: &PrivateFieldExpression<'a>) { + self.visit_expression(&it.object); + } + + #[inline] + fn visit_call_expression(&mut self, it: &CallExpression<'a>) { + self.visit_expression(&it.callee); + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + self.visit_arguments(&it.arguments); + } + + #[inline] + fn visit_new_expression(&mut self, it: &NewExpression<'a>) { + self.visit_expression(&it.callee); + self.visit_arguments(&it.arguments); + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline(always)] + fn visit_meta_property(&mut self, it: &MetaProperty<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_spread_element(&mut self, it: &SpreadElement<'a>) { + self.visit_expression(&it.argument); + } + + fn visit_argument(&mut self, it: &Argument<'a>) { + match it { + Argument::SpreadElement(it) => self.visit_spread_element(it), + Argument::TemplateLiteral(it) => self.visit_template_literal(it), + Argument::ArrayExpression(it) => self.visit_array_expression(it), + Argument::ArrowFunctionExpression(it) => self.visit_arrow_function_expression(it), + Argument::AssignmentExpression(it) => self.visit_assignment_expression(it), + Argument::AwaitExpression(it) => self.visit_await_expression(it), + Argument::BinaryExpression(it) => self.visit_binary_expression(it), + Argument::CallExpression(it) => self.visit_call_expression(it), + Argument::ChainExpression(it) => self.visit_chain_expression(it), + Argument::ClassExpression(it) => self.visit_class(it), + Argument::ConditionalExpression(it) => self.visit_conditional_expression(it), + Argument::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + Argument::ImportExpression(it) => self.visit_import_expression(it), + Argument::LogicalExpression(it) => self.visit_logical_expression(it), + Argument::NewExpression(it) => self.visit_new_expression(it), + Argument::ObjectExpression(it) => self.visit_object_expression(it), + Argument::ParenthesizedExpression(it) => self.visit_parenthesized_expression(it), + Argument::SequenceExpression(it) => self.visit_sequence_expression(it), + Argument::TaggedTemplateExpression(it) => self.visit_tagged_template_expression(it), + Argument::UnaryExpression(it) => self.visit_unary_expression(it), + Argument::UpdateExpression(it) => self.visit_update_expression(it), + Argument::YieldExpression(it) => self.visit_yield_expression(it), + Argument::PrivateInExpression(it) => self.visit_private_in_expression(it), + Argument::JSXElement(it) => self.visit_jsx_element(it), + Argument::JSXFragment(it) => self.visit_jsx_fragment(it), + Argument::TSAsExpression(it) => self.visit_ts_as_expression(it), + Argument::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + Argument::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + Argument::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + Argument::TSInstantiationExpression(it) => self.visit_ts_instantiation_expression(it), + Argument::V8IntrinsicExpression(it) => self.visit_v_8_intrinsic_expression(it), + Argument::ComputedMemberExpression(it) => self.visit_computed_member_expression(it), + Argument::StaticMemberExpression(it) => self.visit_static_member_expression(it), + Argument::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + _ => { + // Remaining variants do not contain scopes: + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + + #[inline] + fn visit_update_expression(&mut self, it: &UpdateExpression<'a>) { + self.visit_simple_assignment_target(&it.argument); + } + + #[inline] + fn visit_unary_expression(&mut self, it: &UnaryExpression<'a>) { + self.visit_expression(&it.argument); + } + + #[inline] + fn visit_binary_expression(&mut self, it: &BinaryExpression<'a>) { + self.visit_expression(&it.left); + self.visit_expression(&it.right); + } + + #[inline] + fn visit_private_in_expression(&mut self, it: &PrivateInExpression<'a>) { + self.visit_expression(&it.right); + } + + #[inline] + fn visit_logical_expression(&mut self, it: &LogicalExpression<'a>) { + self.visit_expression(&it.left); + self.visit_expression(&it.right); + } + + #[inline] + fn visit_conditional_expression(&mut self, it: &ConditionalExpression<'a>) { + self.visit_expression(&it.test); + self.visit_expression(&it.consequent); + self.visit_expression(&it.alternate); + } + + #[inline] + fn visit_assignment_expression(&mut self, it: &AssignmentExpression<'a>) { + self.visit_assignment_target(&it.left); + self.visit_expression(&it.right); + } + + fn visit_assignment_target(&mut self, it: &AssignmentTarget<'a>) { + match it { + AssignmentTarget::TSAsExpression(it) => self.visit_ts_as_expression(it), + AssignmentTarget::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + AssignmentTarget::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + AssignmentTarget::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + AssignmentTarget::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + AssignmentTarget::StaticMemberExpression(it) => self.visit_static_member_expression(it), + AssignmentTarget::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + AssignmentTarget::ArrayAssignmentTarget(it) => self.visit_array_assignment_target(it), + AssignmentTarget::ObjectAssignmentTarget(it) => self.visit_object_assignment_target(it), + _ => { + // Remaining variants do not contain scopes: + // `AssignmentTargetIdentifier` + } + } + } + + fn visit_simple_assignment_target(&mut self, it: &SimpleAssignmentTarget<'a>) { + match it { + SimpleAssignmentTarget::TSAsExpression(it) => self.visit_ts_as_expression(it), + SimpleAssignmentTarget::TSSatisfiesExpression(it) => { + self.visit_ts_satisfies_expression(it) + } + SimpleAssignmentTarget::TSNonNullExpression(it) => { + self.visit_ts_non_null_expression(it) + } + SimpleAssignmentTarget::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + SimpleAssignmentTarget::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + SimpleAssignmentTarget::StaticMemberExpression(it) => { + self.visit_static_member_expression(it) + } + SimpleAssignmentTarget::PrivateFieldExpression(it) => { + self.visit_private_field_expression(it) + } + _ => { + // Remaining variants do not contain scopes: + // `AssignmentTargetIdentifier` + } + } + } + + #[inline] + fn visit_array_assignment_target(&mut self, it: &ArrayAssignmentTarget<'a>) { + for el in it.elements.iter().flatten() { + self.visit_assignment_target_maybe_default(el); + } + if let Some(rest) = &it.rest { + self.visit_assignment_target_rest(rest); + } + } + + #[inline] + fn visit_object_assignment_target(&mut self, it: &ObjectAssignmentTarget<'a>) { + self.visit_assignment_target_properties(&it.properties); + if let Some(rest) = &it.rest { + self.visit_assignment_target_rest(rest); + } + } + + #[inline] + fn visit_assignment_target_rest(&mut self, it: &AssignmentTargetRest<'a>) { + self.visit_assignment_target(&it.target); + } + + fn visit_assignment_target_maybe_default(&mut self, it: &AssignmentTargetMaybeDefault<'a>) { + match it { + AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(it) => { + self.visit_assignment_target_with_default(it) + } + AssignmentTargetMaybeDefault::TSAsExpression(it) => self.visit_ts_as_expression(it), + AssignmentTargetMaybeDefault::TSSatisfiesExpression(it) => { + self.visit_ts_satisfies_expression(it) + } + AssignmentTargetMaybeDefault::TSNonNullExpression(it) => { + self.visit_ts_non_null_expression(it) + } + AssignmentTargetMaybeDefault::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + AssignmentTargetMaybeDefault::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + AssignmentTargetMaybeDefault::StaticMemberExpression(it) => { + self.visit_static_member_expression(it) + } + AssignmentTargetMaybeDefault::PrivateFieldExpression(it) => { + self.visit_private_field_expression(it) + } + AssignmentTargetMaybeDefault::ArrayAssignmentTarget(it) => { + self.visit_array_assignment_target(it) + } + AssignmentTargetMaybeDefault::ObjectAssignmentTarget(it) => { + self.visit_object_assignment_target(it) + } + _ => { + // Remaining variants do not contain scopes: + // `AssignmentTargetIdentifier` + } + } + } + + #[inline] + fn visit_assignment_target_with_default(&mut self, it: &AssignmentTargetWithDefault<'a>) { + self.visit_assignment_target(&it.binding); + self.visit_expression(&it.init); + } + + #[inline] + fn visit_assignment_target_property_identifier( + &mut self, + it: &AssignmentTargetPropertyIdentifier<'a>, + ) { + if let Some(init) = &it.init { + self.visit_expression(init); + } + } + + #[inline] + fn visit_assignment_target_property_property( + &mut self, + it: &AssignmentTargetPropertyProperty<'a>, + ) { + self.visit_property_key(&it.name); + self.visit_assignment_target_maybe_default(&it.binding); + } + + #[inline] + fn visit_sequence_expression(&mut self, it: &SequenceExpression<'a>) { + self.visit_expressions(&it.expressions); + } + + #[inline(always)] + fn visit_super(&mut self, it: &Super) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_await_expression(&mut self, it: &AwaitExpression<'a>) { + self.visit_expression(&it.argument); + } + + #[inline] + fn visit_chain_expression(&mut self, it: &ChainExpression<'a>) { + self.visit_chain_element(&it.expression); + } + + #[inline] + fn visit_parenthesized_expression(&mut self, it: &ParenthesizedExpression<'a>) { + self.visit_expression(&it.expression); + } + + fn visit_statement(&mut self, it: &Statement<'a>) { + match it { + Statement::BlockStatement(it) => self.visit_block_statement(it), + Statement::DoWhileStatement(it) => self.visit_do_while_statement(it), + Statement::ExpressionStatement(it) => self.visit_expression_statement(it), + Statement::ForInStatement(it) => self.visit_for_in_statement(it), + Statement::ForOfStatement(it) => self.visit_for_of_statement(it), + Statement::ForStatement(it) => self.visit_for_statement(it), + Statement::IfStatement(it) => self.visit_if_statement(it), + Statement::LabeledStatement(it) => self.visit_labeled_statement(it), + Statement::ReturnStatement(it) => self.visit_return_statement(it), + Statement::SwitchStatement(it) => self.visit_switch_statement(it), + Statement::ThrowStatement(it) => self.visit_throw_statement(it), + Statement::TryStatement(it) => self.visit_try_statement(it), + Statement::WhileStatement(it) => self.visit_while_statement(it), + Statement::WithStatement(it) => self.visit_with_statement(it), + Statement::VariableDeclaration(it) => self.visit_variable_declaration(it), + Statement::FunctionDeclaration(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + Statement::ClassDeclaration(it) => self.visit_class(it), + Statement::TSTypeAliasDeclaration(it) => self.visit_ts_type_alias_declaration(it), + Statement::TSInterfaceDeclaration(it) => self.visit_ts_interface_declaration(it), + Statement::TSEnumDeclaration(it) => self.visit_ts_enum_declaration(it), + Statement::TSModuleDeclaration(it) => self.visit_ts_module_declaration(it), + Statement::ExportDefaultDeclaration(it) => self.visit_export_default_declaration(it), + Statement::ExportNamedDeclaration(it) => self.visit_export_named_declaration(it), + Statement::TSExportAssignment(it) => self.visit_ts_export_assignment(it), + _ => { + // Remaining variants do not contain scopes: + // `BreakStatement` + // `ContinueStatement` + // `DebuggerStatement` + // `EmptyStatement` + // `TSImportEqualsDeclaration` + // `ImportDeclaration` + // `ExportAllDeclaration` + // `TSNamespaceExportDeclaration` + } + } + } + + #[inline(always)] + fn visit_directive(&mut self, it: &Directive<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_hashbang(&mut self, it: &Hashbang<'a>) { + // Struct does not contain a scope. Halt traversal. + } + #[inline] fn visit_block_statement(&mut self, it: &BlockStatement<'a>) { self.add_scope(&it.scope_id); } + fn visit_declaration(&mut self, it: &Declaration<'a>) { + match it { + Declaration::VariableDeclaration(it) => self.visit_variable_declaration(it), + Declaration::FunctionDeclaration(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + Declaration::ClassDeclaration(it) => self.visit_class(it), + Declaration::TSTypeAliasDeclaration(it) => self.visit_ts_type_alias_declaration(it), + Declaration::TSInterfaceDeclaration(it) => self.visit_ts_interface_declaration(it), + Declaration::TSEnumDeclaration(it) => self.visit_ts_enum_declaration(it), + Declaration::TSModuleDeclaration(it) => self.visit_ts_module_declaration(it), + _ => { + // Remaining variants do not contain scopes: + // `TSImportEqualsDeclaration` + } + } + } + + #[inline] + fn visit_variable_declaration(&mut self, it: &VariableDeclaration<'a>) { + self.visit_variable_declarators(&it.declarations); + } + + #[inline] + fn visit_variable_declarator(&mut self, it: &VariableDeclarator<'a>) { + self.visit_binding_pattern(&it.id); + if let Some(init) = &it.init { + self.visit_expression(init); + } + } + + #[inline(always)] + fn visit_empty_statement(&mut self, it: &EmptyStatement) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_expression_statement(&mut self, it: &ExpressionStatement<'a>) { + self.visit_expression(&it.expression); + } + + #[inline] + fn visit_if_statement(&mut self, it: &IfStatement<'a>) { + self.visit_expression(&it.test); + self.visit_statement(&it.consequent); + if let Some(alternate) = &it.alternate { + self.visit_statement(alternate); + } + } + + #[inline] + fn visit_do_while_statement(&mut self, it: &DoWhileStatement<'a>) { + self.visit_statement(&it.body); + self.visit_expression(&it.test); + } + + #[inline] + fn visit_while_statement(&mut self, it: &WhileStatement<'a>) { + self.visit_expression(&it.test); + self.visit_statement(&it.body); + } + #[inline] fn visit_for_statement(&mut self, it: &ForStatement<'a>) { self.add_scope(&it.scope_id); } + fn visit_for_statement_init(&mut self, it: &ForStatementInit<'a>) { + match it { + ForStatementInit::VariableDeclaration(it) => self.visit_variable_declaration(it), + ForStatementInit::TemplateLiteral(it) => self.visit_template_literal(it), + ForStatementInit::ArrayExpression(it) => self.visit_array_expression(it), + ForStatementInit::ArrowFunctionExpression(it) => { + self.visit_arrow_function_expression(it) + } + ForStatementInit::AssignmentExpression(it) => self.visit_assignment_expression(it), + ForStatementInit::AwaitExpression(it) => self.visit_await_expression(it), + ForStatementInit::BinaryExpression(it) => self.visit_binary_expression(it), + ForStatementInit::CallExpression(it) => self.visit_call_expression(it), + ForStatementInit::ChainExpression(it) => self.visit_chain_expression(it), + ForStatementInit::ClassExpression(it) => self.visit_class(it), + ForStatementInit::ConditionalExpression(it) => self.visit_conditional_expression(it), + ForStatementInit::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + ForStatementInit::ImportExpression(it) => self.visit_import_expression(it), + ForStatementInit::LogicalExpression(it) => self.visit_logical_expression(it), + ForStatementInit::NewExpression(it) => self.visit_new_expression(it), + ForStatementInit::ObjectExpression(it) => self.visit_object_expression(it), + ForStatementInit::ParenthesizedExpression(it) => { + self.visit_parenthesized_expression(it) + } + ForStatementInit::SequenceExpression(it) => self.visit_sequence_expression(it), + ForStatementInit::TaggedTemplateExpression(it) => { + self.visit_tagged_template_expression(it) + } + ForStatementInit::UnaryExpression(it) => self.visit_unary_expression(it), + ForStatementInit::UpdateExpression(it) => self.visit_update_expression(it), + ForStatementInit::YieldExpression(it) => self.visit_yield_expression(it), + ForStatementInit::PrivateInExpression(it) => self.visit_private_in_expression(it), + ForStatementInit::JSXElement(it) => self.visit_jsx_element(it), + ForStatementInit::JSXFragment(it) => self.visit_jsx_fragment(it), + ForStatementInit::TSAsExpression(it) => self.visit_ts_as_expression(it), + ForStatementInit::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + ForStatementInit::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + ForStatementInit::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + ForStatementInit::TSInstantiationExpression(it) => { + self.visit_ts_instantiation_expression(it) + } + ForStatementInit::V8IntrinsicExpression(it) => self.visit_v_8_intrinsic_expression(it), + ForStatementInit::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + ForStatementInit::StaticMemberExpression(it) => self.visit_static_member_expression(it), + ForStatementInit::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + _ => { + // Remaining variants do not contain scopes: + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + #[inline] fn visit_for_in_statement(&mut self, it: &ForInStatement<'a>) { self.add_scope(&it.scope_id); } + fn visit_for_statement_left(&mut self, it: &ForStatementLeft<'a>) { + match it { + ForStatementLeft::VariableDeclaration(it) => self.visit_variable_declaration(it), + ForStatementLeft::TSAsExpression(it) => self.visit_ts_as_expression(it), + ForStatementLeft::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + ForStatementLeft::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + ForStatementLeft::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + ForStatementLeft::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + ForStatementLeft::StaticMemberExpression(it) => self.visit_static_member_expression(it), + ForStatementLeft::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + ForStatementLeft::ArrayAssignmentTarget(it) => self.visit_array_assignment_target(it), + ForStatementLeft::ObjectAssignmentTarget(it) => self.visit_object_assignment_target(it), + _ => { + // Remaining variants do not contain scopes: + // `AssignmentTargetIdentifier` + } + } + } + #[inline] fn visit_for_of_statement(&mut self, it: &ForOfStatement<'a>) { self.add_scope(&it.scope_id); } + #[inline(always)] + fn visit_continue_statement(&mut self, it: &ContinueStatement<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_break_statement(&mut self, it: &BreakStatement<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_return_statement(&mut self, it: &ReturnStatement<'a>) { + if let Some(argument) = &it.argument { + self.visit_expression(argument); + } + } + + #[inline] + fn visit_with_statement(&mut self, it: &WithStatement<'a>) { + self.visit_expression(&it.object); + self.visit_statement(&it.body); + } + #[inline] fn visit_switch_statement(&mut self, it: &SwitchStatement<'a>) { + self.visit_expression(&it.discriminant); self.add_scope(&it.scope_id); } + #[inline] + fn visit_switch_case(&mut self, it: &SwitchCase<'a>) { + if let Some(test) = &it.test { + self.visit_expression(test); + } + self.visit_statements(&it.consequent); + } + + #[inline] + fn visit_labeled_statement(&mut self, it: &LabeledStatement<'a>) { + self.visit_statement(&it.body); + } + + #[inline] + fn visit_throw_statement(&mut self, it: &ThrowStatement<'a>) { + self.visit_expression(&it.argument); + } + + #[inline] + fn visit_try_statement(&mut self, it: &TryStatement<'a>) { + self.visit_block_statement(&it.block); + if let Some(handler) = &it.handler { + self.visit_catch_clause(handler); + } + if let Some(finalizer) = &it.finalizer { + self.visit_block_statement(finalizer); + } + } + #[inline] fn visit_catch_clause(&mut self, it: &CatchClause<'a>) { self.add_scope(&it.scope_id); } + #[inline] + fn visit_catch_parameter(&mut self, it: &CatchParameter<'a>) { + self.visit_binding_pattern(&it.pattern); + } + + #[inline(always)] + fn visit_debugger_statement(&mut self, it: &DebuggerStatement) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_binding_pattern(&mut self, it: &BindingPattern<'a>) { + self.visit_binding_pattern_kind(&it.kind); + if let Some(type_annotation) = &it.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } + } + + #[inline] + fn visit_binding_pattern_kind(&mut self, it: &BindingPatternKind<'a>) { + match it { + BindingPatternKind::ObjectPattern(it) => self.visit_object_pattern(it), + BindingPatternKind::ArrayPattern(it) => self.visit_array_pattern(it), + BindingPatternKind::AssignmentPattern(it) => self.visit_assignment_pattern(it), + _ => { + // Remaining variants do not contain scopes: + // `BindingIdentifier` + } + } + } + + #[inline] + fn visit_assignment_pattern(&mut self, it: &AssignmentPattern<'a>) { + self.visit_binding_pattern(&it.left); + self.visit_expression(&it.right); + } + + #[inline] + fn visit_object_pattern(&mut self, it: &ObjectPattern<'a>) { + self.visit_binding_properties(&it.properties); + if let Some(rest) = &it.rest { + self.visit_binding_rest_element(rest); + } + } + + #[inline] + fn visit_binding_property(&mut self, it: &BindingProperty<'a>) { + self.visit_property_key(&it.key); + self.visit_binding_pattern(&it.value); + } + + #[inline] + fn visit_array_pattern(&mut self, it: &ArrayPattern<'a>) { + for el in it.elements.iter().flatten() { + self.visit_binding_pattern(el); + } + if let Some(rest) = &it.rest { + self.visit_binding_rest_element(rest); + } + } + + #[inline] + fn visit_binding_rest_element(&mut self, it: &BindingRestElement<'a>) { + self.visit_binding_pattern(&it.argument); + } + #[inline] fn visit_function(&mut self, it: &Function<'a>, _: ScopeFlags) { self.add_scope(&it.scope_id); } + #[inline] + fn visit_formal_parameters(&mut self, it: &FormalParameters<'a>) { + self.visit_formal_parameter_list(&it.items); + if let Some(rest) = &it.rest { + self.visit_binding_rest_element(rest); + } + } + + #[inline] + fn visit_formal_parameter(&mut self, it: &FormalParameter<'a>) { + self.visit_decorators(&it.decorators); + self.visit_binding_pattern(&it.pattern); + } + + #[inline] + fn visit_function_body(&mut self, it: &FunctionBody<'a>) { + self.visit_statements(&it.statements); + } + #[inline] fn visit_arrow_function_expression(&mut self, it: &ArrowFunctionExpression<'a>) { self.add_scope(&it.scope_id); } #[inline] - fn visit_class(&mut self, it: &Class<'a>) { - self.add_scope(&it.scope_id); + fn visit_yield_expression(&mut self, it: &YieldExpression<'a>) { + if let Some(argument) = &it.argument { + self.visit_expression(argument); + } } #[inline] - fn visit_static_block(&mut self, it: &StaticBlock<'a>) { + fn visit_class(&mut self, it: &Class<'a>) { + self.visit_decorators(&it.decorators); self.add_scope(&it.scope_id); } #[inline] - fn visit_ts_enum_declaration(&mut self, it: &TSEnumDeclaration<'a>) { - self.add_scope(&it.scope_id); + fn visit_class_body(&mut self, it: &ClassBody<'a>) { + self.visit_class_elements(&it.body); } #[inline] - fn visit_ts_conditional_type(&mut self, it: &TSConditionalType<'a>) { - self.add_scope(&it.scope_id); + fn visit_method_definition(&mut self, it: &MethodDefinition<'a>) { + self.visit_decorators(&it.decorators); + self.visit_property_key(&it.key); + { + let flags = match it.kind { + MethodDefinitionKind::Get => ScopeFlags::Function | ScopeFlags::GetAccessor, + MethodDefinitionKind::Set => ScopeFlags::Function | ScopeFlags::SetAccessor, + MethodDefinitionKind::Constructor => ScopeFlags::Function | ScopeFlags::Constructor, + MethodDefinitionKind::Method => ScopeFlags::Function, + }; + self.visit_function(&it.value, flags); + } } #[inline] - fn visit_ts_type_alias_declaration(&mut self, it: &TSTypeAliasDeclaration<'a>) { - self.add_scope(&it.scope_id); + fn visit_property_definition(&mut self, it: &PropertyDefinition<'a>) { + self.visit_decorators(&it.decorators); + self.visit_property_key(&it.key); + if let Some(value) = &it.value { + self.visit_expression(value); + } + if let Some(type_annotation) = &it.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } + } + + #[inline(always)] + fn visit_private_identifier(&mut self, it: &PrivateIdentifier<'a>) { + // Struct does not contain a scope. Halt traversal. } #[inline] - fn visit_ts_interface_declaration(&mut self, it: &TSInterfaceDeclaration<'a>) { + fn visit_static_block(&mut self, it: &StaticBlock<'a>) { self.add_scope(&it.scope_id); } #[inline] - fn visit_ts_method_signature(&mut self, it: &TSMethodSignature<'a>) { - self.add_scope(&it.scope_id); + fn visit_module_declaration(&mut self, it: &ModuleDeclaration<'a>) { + match it { + ModuleDeclaration::ExportDefaultDeclaration(it) => { + self.visit_export_default_declaration(it) + } + ModuleDeclaration::ExportNamedDeclaration(it) => { + self.visit_export_named_declaration(it) + } + ModuleDeclaration::TSExportAssignment(it) => self.visit_ts_export_assignment(it), + _ => { + // Remaining variants do not contain scopes: + // `ImportDeclaration` + // `ExportAllDeclaration` + // `TSNamespaceExportDeclaration` + } + } } #[inline] - fn visit_ts_construct_signature_declaration( - &mut self, - it: &TSConstructSignatureDeclaration<'a>, - ) { - self.add_scope(&it.scope_id); + fn visit_accessor_property(&mut self, it: &AccessorProperty<'a>) { + self.visit_decorators(&it.decorators); + self.visit_property_key(&it.key); + if let Some(value) = &it.value { + self.visit_expression(value); + } + if let Some(type_annotation) = &it.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } } #[inline] - fn visit_ts_module_declaration(&mut self, it: &TSModuleDeclaration<'a>) { - self.add_scope(&it.scope_id); + fn visit_import_expression(&mut self, it: &ImportExpression<'a>) { + self.visit_expression(&it.source); + self.visit_expressions(&it.options); + } + + #[inline(always)] + fn visit_import_declaration(&mut self, it: &ImportDeclaration<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_import_declaration_specifier(&mut self, it: &ImportDeclarationSpecifier<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_import_specifier(&mut self, it: &ImportSpecifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_import_default_specifier(&mut self, it: &ImportDefaultSpecifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_import_namespace_specifier(&mut self, it: &ImportNamespaceSpecifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_with_clause(&mut self, it: &WithClause<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_import_attribute(&mut self, it: &ImportAttribute<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_import_attribute_key(&mut self, it: &ImportAttributeKey<'a>) { + // Enum does not contain a scope. Halt traversal. } #[inline] - fn visit_ts_function_type(&mut self, it: &TSFunctionType<'a>) { - self.add_scope(&it.scope_id); + fn visit_export_named_declaration(&mut self, it: &ExportNamedDeclaration<'a>) { + if let Some(declaration) = &it.declaration { + self.visit_declaration(declaration); + } } #[inline] - fn visit_ts_mapped_type(&mut self, it: &TSMappedType<'a>) { - self.add_scope(&it.scope_id); + fn visit_export_default_declaration(&mut self, it: &ExportDefaultDeclaration<'a>) { + self.visit_export_default_declaration_kind(&it.declaration); + } + + #[inline(always)] + fn visit_export_all_declaration(&mut self, it: &ExportAllDeclaration<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_export_specifier(&mut self, it: &ExportSpecifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + fn visit_export_default_declaration_kind(&mut self, it: &ExportDefaultDeclarationKind<'a>) { + match it { + ExportDefaultDeclarationKind::FunctionDeclaration(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + ExportDefaultDeclarationKind::ClassDeclaration(it) => self.visit_class(it), + ExportDefaultDeclarationKind::TSInterfaceDeclaration(it) => { + self.visit_ts_interface_declaration(it) + } + ExportDefaultDeclarationKind::TemplateLiteral(it) => self.visit_template_literal(it), + ExportDefaultDeclarationKind::ArrayExpression(it) => self.visit_array_expression(it), + ExportDefaultDeclarationKind::ArrowFunctionExpression(it) => { + self.visit_arrow_function_expression(it) + } + ExportDefaultDeclarationKind::AssignmentExpression(it) => { + self.visit_assignment_expression(it) + } + ExportDefaultDeclarationKind::AwaitExpression(it) => self.visit_await_expression(it), + ExportDefaultDeclarationKind::BinaryExpression(it) => self.visit_binary_expression(it), + ExportDefaultDeclarationKind::CallExpression(it) => self.visit_call_expression(it), + ExportDefaultDeclarationKind::ChainExpression(it) => self.visit_chain_expression(it), + ExportDefaultDeclarationKind::ClassExpression(it) => self.visit_class(it), + ExportDefaultDeclarationKind::ConditionalExpression(it) => { + self.visit_conditional_expression(it) + } + ExportDefaultDeclarationKind::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + ExportDefaultDeclarationKind::ImportExpression(it) => self.visit_import_expression(it), + ExportDefaultDeclarationKind::LogicalExpression(it) => { + self.visit_logical_expression(it) + } + ExportDefaultDeclarationKind::NewExpression(it) => self.visit_new_expression(it), + ExportDefaultDeclarationKind::ObjectExpression(it) => self.visit_object_expression(it), + ExportDefaultDeclarationKind::ParenthesizedExpression(it) => { + self.visit_parenthesized_expression(it) + } + ExportDefaultDeclarationKind::SequenceExpression(it) => { + self.visit_sequence_expression(it) + } + ExportDefaultDeclarationKind::TaggedTemplateExpression(it) => { + self.visit_tagged_template_expression(it) + } + ExportDefaultDeclarationKind::UnaryExpression(it) => self.visit_unary_expression(it), + ExportDefaultDeclarationKind::UpdateExpression(it) => self.visit_update_expression(it), + ExportDefaultDeclarationKind::YieldExpression(it) => self.visit_yield_expression(it), + ExportDefaultDeclarationKind::PrivateInExpression(it) => { + self.visit_private_in_expression(it) + } + ExportDefaultDeclarationKind::JSXElement(it) => self.visit_jsx_element(it), + ExportDefaultDeclarationKind::JSXFragment(it) => self.visit_jsx_fragment(it), + ExportDefaultDeclarationKind::TSAsExpression(it) => self.visit_ts_as_expression(it), + ExportDefaultDeclarationKind::TSSatisfiesExpression(it) => { + self.visit_ts_satisfies_expression(it) + } + ExportDefaultDeclarationKind::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + ExportDefaultDeclarationKind::TSNonNullExpression(it) => { + self.visit_ts_non_null_expression(it) + } + ExportDefaultDeclarationKind::TSInstantiationExpression(it) => { + self.visit_ts_instantiation_expression(it) + } + ExportDefaultDeclarationKind::V8IntrinsicExpression(it) => { + self.visit_v_8_intrinsic_expression(it) + } + ExportDefaultDeclarationKind::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + ExportDefaultDeclarationKind::StaticMemberExpression(it) => { + self.visit_static_member_expression(it) + } + ExportDefaultDeclarationKind::PrivateFieldExpression(it) => { + self.visit_private_field_expression(it) + } + _ => { + // Remaining variants do not contain scopes: + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + + #[inline(always)] + fn visit_module_export_name(&mut self, it: &ModuleExportName<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_v_8_intrinsic_expression(&mut self, it: &V8IntrinsicExpression<'a>) { + self.visit_arguments(&it.arguments); + } + + #[inline(always)] + fn visit_boolean_literal(&mut self, it: &BooleanLiteral) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_null_literal(&mut self, it: &NullLiteral) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_numeric_literal(&mut self, it: &NumericLiteral<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_string_literal(&mut self, it: &StringLiteral<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_big_int_literal(&mut self, it: &BigIntLiteral<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_reg_exp_literal(&mut self, it: &RegExpLiteral<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_jsx_element(&mut self, it: &JSXElement<'a>) { + self.visit_jsx_opening_element(&it.opening_element); + self.visit_jsx_children(&it.children); + } + + #[inline] + fn visit_jsx_opening_element(&mut self, it: &JSXOpeningElement<'a>) { + self.visit_jsx_attribute_items(&it.attributes); + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline(always)] + fn visit_jsx_closing_element(&mut self, it: &JSXClosingElement<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_jsx_fragment(&mut self, it: &JSXFragment<'a>) { + self.visit_jsx_children(&it.children); + } + + #[inline(always)] + fn visit_jsx_opening_fragment(&mut self, it: &JSXOpeningFragment) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_jsx_closing_fragment(&mut self, it: &JSXClosingFragment) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_jsx_element_name(&mut self, it: &JSXElementName<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_jsx_namespaced_name(&mut self, it: &JSXNamespacedName<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_jsx_member_expression(&mut self, it: &JSXMemberExpression<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_jsx_member_expression_object(&mut self, it: &JSXMemberExpressionObject<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_jsx_expression_container(&mut self, it: &JSXExpressionContainer<'a>) { + self.visit_jsx_expression(&it.expression); + } + + fn visit_jsx_expression(&mut self, it: &JSXExpression<'a>) { + match it { + JSXExpression::TemplateLiteral(it) => self.visit_template_literal(it), + JSXExpression::ArrayExpression(it) => self.visit_array_expression(it), + JSXExpression::ArrowFunctionExpression(it) => self.visit_arrow_function_expression(it), + JSXExpression::AssignmentExpression(it) => self.visit_assignment_expression(it), + JSXExpression::AwaitExpression(it) => self.visit_await_expression(it), + JSXExpression::BinaryExpression(it) => self.visit_binary_expression(it), + JSXExpression::CallExpression(it) => self.visit_call_expression(it), + JSXExpression::ChainExpression(it) => self.visit_chain_expression(it), + JSXExpression::ClassExpression(it) => self.visit_class(it), + JSXExpression::ConditionalExpression(it) => self.visit_conditional_expression(it), + JSXExpression::FunctionExpression(it) => { + let flags = ScopeFlags::Function; + self.visit_function(it, flags) + } + JSXExpression::ImportExpression(it) => self.visit_import_expression(it), + JSXExpression::LogicalExpression(it) => self.visit_logical_expression(it), + JSXExpression::NewExpression(it) => self.visit_new_expression(it), + JSXExpression::ObjectExpression(it) => self.visit_object_expression(it), + JSXExpression::ParenthesizedExpression(it) => self.visit_parenthesized_expression(it), + JSXExpression::SequenceExpression(it) => self.visit_sequence_expression(it), + JSXExpression::TaggedTemplateExpression(it) => { + self.visit_tagged_template_expression(it) + } + JSXExpression::UnaryExpression(it) => self.visit_unary_expression(it), + JSXExpression::UpdateExpression(it) => self.visit_update_expression(it), + JSXExpression::YieldExpression(it) => self.visit_yield_expression(it), + JSXExpression::PrivateInExpression(it) => self.visit_private_in_expression(it), + JSXExpression::JSXElement(it) => self.visit_jsx_element(it), + JSXExpression::JSXFragment(it) => self.visit_jsx_fragment(it), + JSXExpression::TSAsExpression(it) => self.visit_ts_as_expression(it), + JSXExpression::TSSatisfiesExpression(it) => self.visit_ts_satisfies_expression(it), + JSXExpression::TSTypeAssertion(it) => self.visit_ts_type_assertion(it), + JSXExpression::TSNonNullExpression(it) => self.visit_ts_non_null_expression(it), + JSXExpression::TSInstantiationExpression(it) => { + self.visit_ts_instantiation_expression(it) + } + JSXExpression::V8IntrinsicExpression(it) => self.visit_v_8_intrinsic_expression(it), + JSXExpression::ComputedMemberExpression(it) => { + self.visit_computed_member_expression(it) + } + JSXExpression::StaticMemberExpression(it) => self.visit_static_member_expression(it), + JSXExpression::PrivateFieldExpression(it) => self.visit_private_field_expression(it), + _ => { + // Remaining variants do not contain scopes: + // `EmptyExpression` + // `BooleanLiteral` + // `NullLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `RegExpLiteral` + // `StringLiteral` + // `Identifier` + // `MetaProperty` + // `Super` + // `ThisExpression` + } + } + } + + #[inline(always)] + fn visit_jsx_empty_expression(&mut self, it: &JSXEmptyExpression) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_jsx_attribute(&mut self, it: &JSXAttribute<'a>) { + if let Some(value) = &it.value { + self.visit_jsx_attribute_value(value); + } + } + + #[inline] + fn visit_jsx_spread_attribute(&mut self, it: &JSXSpreadAttribute<'a>) { + self.visit_expression(&it.argument); + } + + #[inline(always)] + fn visit_jsx_attribute_name(&mut self, it: &JSXAttributeName<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_jsx_attribute_value(&mut self, it: &JSXAttributeValue<'a>) { + match it { + JSXAttributeValue::ExpressionContainer(it) => self.visit_jsx_expression_container(it), + JSXAttributeValue::Element(it) => self.visit_jsx_element(it), + JSXAttributeValue::Fragment(it) => self.visit_jsx_fragment(it), + _ => { + // Remaining variants do not contain scopes: + // `StringLiteral` + } + } + } + + #[inline(always)] + fn visit_jsx_identifier(&mut self, it: &JSXIdentifier<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_jsx_child(&mut self, it: &JSXChild<'a>) { + match it { + JSXChild::Element(it) => self.visit_jsx_element(it), + JSXChild::Fragment(it) => self.visit_jsx_fragment(it), + JSXChild::ExpressionContainer(it) => self.visit_jsx_expression_container(it), + JSXChild::Spread(it) => self.visit_jsx_spread_child(it), + _ => { + // Remaining variants do not contain scopes: + // `Text` + } + } + } + + #[inline] + fn visit_jsx_spread_child(&mut self, it: &JSXSpreadChild<'a>) { + self.visit_expression(&it.expression); + } + + #[inline(always)] + fn visit_jsx_text(&mut self, it: &JSXText<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_this_parameter(&mut self, it: &TSThisParameter<'a>) { + if let Some(type_annotation) = &it.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } + } + + #[inline] + fn visit_ts_enum_declaration(&mut self, it: &TSEnumDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_enum_body(&mut self, it: &TSEnumBody<'a>) { + self.visit_ts_enum_members(&it.members); + } + + #[inline] + fn visit_ts_enum_member(&mut self, it: &TSEnumMember<'a>) { + self.visit_ts_enum_member_name(&it.id); + if let Some(initializer) = &it.initializer { + self.visit_expression(initializer); + } + } + + #[inline] + fn visit_ts_enum_member_name(&mut self, it: &TSEnumMemberName<'a>) { + match it { + TSEnumMemberName::TemplateString(it) => self.visit_template_literal(it), + _ => { + // Remaining variants do not contain scopes: + // `Identifier` + // `String` + } + } + } + + #[inline] + fn visit_ts_type_annotation(&mut self, it: &TSTypeAnnotation<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_ts_literal_type(&mut self, it: &TSLiteralType<'a>) { + self.visit_ts_literal(&it.literal); + } + + #[inline] + fn visit_ts_literal(&mut self, it: &TSLiteral<'a>) { + match it { + TSLiteral::TemplateLiteral(it) => self.visit_template_literal(it), + TSLiteral::UnaryExpression(it) => self.visit_unary_expression(it), + _ => { + // Remaining variants do not contain scopes: + // `BooleanLiteral` + // `NumericLiteral` + // `BigIntLiteral` + // `StringLiteral` + } + } + } + + fn visit_ts_type(&mut self, it: &TSType<'a>) { + match it { + TSType::TSArrayType(it) => self.visit_ts_array_type(it), + TSType::TSConditionalType(it) => self.visit_ts_conditional_type(it), + TSType::TSConstructorType(it) => self.visit_ts_constructor_type(it), + TSType::TSFunctionType(it) => self.visit_ts_function_type(it), + TSType::TSImportType(it) => self.visit_ts_import_type(it), + TSType::TSIndexedAccessType(it) => self.visit_ts_indexed_access_type(it), + TSType::TSInferType(it) => self.visit_ts_infer_type(it), + TSType::TSIntersectionType(it) => self.visit_ts_intersection_type(it), + TSType::TSLiteralType(it) => self.visit_ts_literal_type(it), + TSType::TSMappedType(it) => self.visit_ts_mapped_type(it), + TSType::TSNamedTupleMember(it) => self.visit_ts_named_tuple_member(it), + TSType::TSTemplateLiteralType(it) => self.visit_ts_template_literal_type(it), + TSType::TSTupleType(it) => self.visit_ts_tuple_type(it), + TSType::TSTypeLiteral(it) => self.visit_ts_type_literal(it), + TSType::TSTypeOperatorType(it) => self.visit_ts_type_operator(it), + TSType::TSTypePredicate(it) => self.visit_ts_type_predicate(it), + TSType::TSTypeQuery(it) => self.visit_ts_type_query(it), + TSType::TSTypeReference(it) => self.visit_ts_type_reference(it), + TSType::TSUnionType(it) => self.visit_ts_union_type(it), + TSType::TSParenthesizedType(it) => self.visit_ts_parenthesized_type(it), + TSType::JSDocNullableType(it) => self.visit_js_doc_nullable_type(it), + TSType::JSDocNonNullableType(it) => self.visit_js_doc_non_nullable_type(it), + _ => { + // Remaining variants do not contain scopes: + // `TSAnyKeyword` + // `TSBigIntKeyword` + // `TSBooleanKeyword` + // `TSIntrinsicKeyword` + // `TSNeverKeyword` + // `TSNullKeyword` + // `TSNumberKeyword` + // `TSObjectKeyword` + // `TSStringKeyword` + // `TSSymbolKeyword` + // `TSUndefinedKeyword` + // `TSUnknownKeyword` + // `TSVoidKeyword` + // `TSThisType` + // `JSDocUnknownType` + } + } + } + + #[inline] + fn visit_ts_conditional_type(&mut self, it: &TSConditionalType<'a>) { + self.visit_ts_type(&it.check_type); + self.add_scope(&it.scope_id); + self.visit_ts_type(&it.false_type); + } + + #[inline] + fn visit_ts_union_type(&mut self, it: &TSUnionType<'a>) { + self.visit_ts_types(&it.types); + } + + #[inline] + fn visit_ts_intersection_type(&mut self, it: &TSIntersectionType<'a>) { + self.visit_ts_types(&it.types); + } + + #[inline] + fn visit_ts_parenthesized_type(&mut self, it: &TSParenthesizedType<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_ts_type_operator(&mut self, it: &TSTypeOperator<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_ts_array_type(&mut self, it: &TSArrayType<'a>) { + self.visit_ts_type(&it.element_type); + } + + #[inline] + fn visit_ts_indexed_access_type(&mut self, it: &TSIndexedAccessType<'a>) { + self.visit_ts_type(&it.object_type); + self.visit_ts_type(&it.index_type); + } + + #[inline] + fn visit_ts_tuple_type(&mut self, it: &TSTupleType<'a>) { + self.visit_ts_tuple_elements(&it.element_types); + } + + #[inline] + fn visit_ts_named_tuple_member(&mut self, it: &TSNamedTupleMember<'a>) { + self.visit_ts_tuple_element(&it.element_type); + } + + #[inline] + fn visit_ts_optional_type(&mut self, it: &TSOptionalType<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_ts_rest_type(&mut self, it: &TSRestType<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + fn visit_ts_tuple_element(&mut self, it: &TSTupleElement<'a>) { + match it { + TSTupleElement::TSOptionalType(it) => self.visit_ts_optional_type(it), + TSTupleElement::TSRestType(it) => self.visit_ts_rest_type(it), + TSTupleElement::TSArrayType(it) => self.visit_ts_array_type(it), + TSTupleElement::TSConditionalType(it) => self.visit_ts_conditional_type(it), + TSTupleElement::TSConstructorType(it) => self.visit_ts_constructor_type(it), + TSTupleElement::TSFunctionType(it) => self.visit_ts_function_type(it), + TSTupleElement::TSImportType(it) => self.visit_ts_import_type(it), + TSTupleElement::TSIndexedAccessType(it) => self.visit_ts_indexed_access_type(it), + TSTupleElement::TSInferType(it) => self.visit_ts_infer_type(it), + TSTupleElement::TSIntersectionType(it) => self.visit_ts_intersection_type(it), + TSTupleElement::TSLiteralType(it) => self.visit_ts_literal_type(it), + TSTupleElement::TSMappedType(it) => self.visit_ts_mapped_type(it), + TSTupleElement::TSNamedTupleMember(it) => self.visit_ts_named_tuple_member(it), + TSTupleElement::TSTemplateLiteralType(it) => self.visit_ts_template_literal_type(it), + TSTupleElement::TSTupleType(it) => self.visit_ts_tuple_type(it), + TSTupleElement::TSTypeLiteral(it) => self.visit_ts_type_literal(it), + TSTupleElement::TSTypeOperatorType(it) => self.visit_ts_type_operator(it), + TSTupleElement::TSTypePredicate(it) => self.visit_ts_type_predicate(it), + TSTupleElement::TSTypeQuery(it) => self.visit_ts_type_query(it), + TSTupleElement::TSTypeReference(it) => self.visit_ts_type_reference(it), + TSTupleElement::TSUnionType(it) => self.visit_ts_union_type(it), + TSTupleElement::TSParenthesizedType(it) => self.visit_ts_parenthesized_type(it), + TSTupleElement::JSDocNullableType(it) => self.visit_js_doc_nullable_type(it), + TSTupleElement::JSDocNonNullableType(it) => self.visit_js_doc_non_nullable_type(it), + _ => { + // Remaining variants do not contain scopes: + // `TSAnyKeyword` + // `TSBigIntKeyword` + // `TSBooleanKeyword` + // `TSIntrinsicKeyword` + // `TSNeverKeyword` + // `TSNullKeyword` + // `TSNumberKeyword` + // `TSObjectKeyword` + // `TSStringKeyword` + // `TSSymbolKeyword` + // `TSUndefinedKeyword` + // `TSUnknownKeyword` + // `TSVoidKeyword` + // `TSThisType` + // `JSDocUnknownType` + } + } + } + + #[inline(always)] + fn visit_ts_any_keyword(&mut self, it: &TSAnyKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_string_keyword(&mut self, it: &TSStringKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_boolean_keyword(&mut self, it: &TSBooleanKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_number_keyword(&mut self, it: &TSNumberKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_never_keyword(&mut self, it: &TSNeverKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_intrinsic_keyword(&mut self, it: &TSIntrinsicKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_unknown_keyword(&mut self, it: &TSUnknownKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_null_keyword(&mut self, it: &TSNullKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_undefined_keyword(&mut self, it: &TSUndefinedKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_void_keyword(&mut self, it: &TSVoidKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_symbol_keyword(&mut self, it: &TSSymbolKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_this_type(&mut self, it: &TSThisType) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_object_keyword(&mut self, it: &TSObjectKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_big_int_keyword(&mut self, it: &TSBigIntKeyword) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_type_reference(&mut self, it: &TSTypeReference<'a>) { + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline(always)] + fn visit_ts_type_name(&mut self, it: &TSTypeName<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_qualified_name(&mut self, it: &TSQualifiedName<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_type_parameter_instantiation(&mut self, it: &TSTypeParameterInstantiation<'a>) { + self.visit_ts_types(&it.params); + } + + #[inline] + fn visit_ts_type_parameter(&mut self, it: &TSTypeParameter<'a>) { + if let Some(constraint) = &it.constraint { + self.visit_ts_type(constraint); + } + if let Some(default) = &it.default { + self.visit_ts_type(default); + } + } + + #[inline] + fn visit_ts_type_parameter_declaration(&mut self, it: &TSTypeParameterDeclaration<'a>) { + self.visit_ts_type_parameters(&it.params); + } + + #[inline] + fn visit_ts_type_alias_declaration(&mut self, it: &TSTypeAliasDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_class_implements(&mut self, it: &TSClassImplements<'a>) { + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline] + fn visit_ts_interface_declaration(&mut self, it: &TSInterfaceDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_interface_body(&mut self, it: &TSInterfaceBody<'a>) { + self.visit_ts_signatures(&it.body); + } + + #[inline] + fn visit_ts_property_signature(&mut self, it: &TSPropertySignature<'a>) { + self.visit_property_key(&it.key); + if let Some(type_annotation) = &it.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } + } + + #[inline] + fn visit_ts_index_signature(&mut self, it: &TSIndexSignature<'a>) { + self.visit_ts_index_signature_names(&it.parameters); + self.visit_ts_type_annotation(&it.type_annotation); + } + + #[inline] + fn visit_ts_call_signature_declaration(&mut self, it: &TSCallSignatureDeclaration<'a>) { + if let Some(type_parameters) = &it.type_parameters { + self.visit_ts_type_parameter_declaration(type_parameters); + } + if let Some(this_param) = &it.this_param { + self.visit_ts_this_parameter(this_param); + } + self.visit_formal_parameters(&it.params); + if let Some(return_type) = &it.return_type { + self.visit_ts_type_annotation(return_type); + } + } + + #[inline] + fn visit_ts_method_signature(&mut self, it: &TSMethodSignature<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_construct_signature_declaration( + &mut self, + it: &TSConstructSignatureDeclaration<'a>, + ) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_index_signature_name(&mut self, it: &TSIndexSignatureName<'a>) { + self.visit_ts_type_annotation(&it.type_annotation); + } + + #[inline] + fn visit_ts_interface_heritage(&mut self, it: &TSInterfaceHeritage<'a>) { + self.visit_expression(&it.expression); + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline] + fn visit_ts_type_predicate(&mut self, it: &TSTypePredicate<'a>) { + if let Some(type_annotation) = &it.type_annotation { + self.visit_ts_type_annotation(type_annotation); + } + } + + #[inline(always)] + fn visit_ts_type_predicate_name(&mut self, it: &TSTypePredicateName<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_module_declaration(&mut self, it: &TSModuleDeclaration<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline(always)] + fn visit_ts_module_declaration_name(&mut self, it: &TSModuleDeclarationName<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_module_block(&mut self, it: &TSModuleBlock<'a>) { + self.visit_statements(&it.body); + } + + #[inline] + fn visit_ts_type_literal(&mut self, it: &TSTypeLiteral<'a>) { + self.visit_ts_signatures(&it.members); + } + + #[inline] + fn visit_ts_infer_type(&mut self, it: &TSInferType<'a>) { + self.visit_ts_type_parameter(&it.type_parameter); + } + + #[inline] + fn visit_ts_type_query(&mut self, it: &TSTypeQuery<'a>) { + self.visit_ts_type_query_expr_name(&it.expr_name); + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline] + fn visit_ts_type_query_expr_name(&mut self, it: &TSTypeQueryExprName<'a>) { + match it { + TSTypeQueryExprName::TSImportType(it) => self.visit_ts_import_type(it), + _ => { + // Remaining variants do not contain scopes: + // `IdentifierReference` + // `QualifiedName` + } + } + } + + #[inline] + fn visit_ts_import_type(&mut self, it: &TSImportType<'a>) { + self.visit_ts_type(&it.argument); + if let Some(options) = &it.options { + self.visit_object_expression(options); + } + if let Some(type_arguments) = &it.type_arguments { + self.visit_ts_type_parameter_instantiation(type_arguments); + } + } + + #[inline] + fn visit_ts_function_type(&mut self, it: &TSFunctionType<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_constructor_type(&mut self, it: &TSConstructorType<'a>) { + if let Some(type_parameters) = &it.type_parameters { + self.visit_ts_type_parameter_declaration(type_parameters); + } + self.visit_formal_parameters(&it.params); + self.visit_ts_type_annotation(&it.return_type); + } + + #[inline] + fn visit_ts_mapped_type(&mut self, it: &TSMappedType<'a>) { + self.add_scope(&it.scope_id); + } + + #[inline] + fn visit_ts_template_literal_type(&mut self, it: &TSTemplateLiteralType<'a>) { + self.visit_ts_types(&it.types); + } + + #[inline] + fn visit_ts_as_expression(&mut self, it: &TSAsExpression<'a>) { + self.visit_expression(&it.expression); + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_ts_satisfies_expression(&mut self, it: &TSSatisfiesExpression<'a>) { + self.visit_expression(&it.expression); + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_ts_type_assertion(&mut self, it: &TSTypeAssertion<'a>) { + self.visit_expression(&it.expression); + self.visit_ts_type(&it.type_annotation); + } + + #[inline(always)] + fn visit_ts_import_equals_declaration(&mut self, it: &TSImportEqualsDeclaration<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_module_reference(&mut self, it: &TSModuleReference<'a>) { + // Enum does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_ts_external_module_reference(&mut self, it: &TSExternalModuleReference<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_non_null_expression(&mut self, it: &TSNonNullExpression<'a>) { + self.visit_expression(&it.expression); + } + + #[inline] + fn visit_decorator(&mut self, it: &Decorator<'a>) { + self.visit_expression(&it.expression); + } + + #[inline] + fn visit_ts_export_assignment(&mut self, it: &TSExportAssignment<'a>) { + self.visit_expression(&it.expression); + } + + #[inline(always)] + fn visit_ts_namespace_export_declaration(&mut self, it: &TSNamespaceExportDeclaration<'a>) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline] + fn visit_ts_instantiation_expression(&mut self, it: &TSInstantiationExpression<'a>) { + self.visit_expression(&it.expression); + self.visit_ts_type_parameter_instantiation(&it.type_arguments); + } + + #[inline] + fn visit_js_doc_nullable_type(&mut self, it: &JSDocNullableType<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + #[inline] + fn visit_js_doc_non_nullable_type(&mut self, it: &JSDocNonNullableType<'a>) { + self.visit_ts_type(&it.type_annotation); + } + + #[inline(always)] + fn visit_js_doc_unknown_type(&mut self, it: &JSDocUnknownType) { + // Struct does not contain a scope. Halt traversal. + } + + #[inline(always)] + fn visit_span(&mut self, it: &Span) { + // Struct does not contain a scope. Halt traversal. } } diff --git a/tasks/ast_tools/src/generators/scopes_collector.rs b/tasks/ast_tools/src/generators/scopes_collector.rs index c998e64ee83f7..a4658ed5f2825 100644 --- a/tasks/ast_tools/src/generators/scopes_collector.rs +++ b/tasks/ast_tools/src/generators/scopes_collector.rs @@ -1,94 +1,399 @@ -//! Generator for `ChildScopesCollector` visitor. +//! Generator for `ChildScopeCollector` visitor. + +use std::iter; use proc_macro2::TokenStream; use quote::quote; +use oxc_index::IndexVec; + use crate::{ Codegen, Generator, TRAVERSE_CRATE_PATH, output::{Output, output_path}, - schema::{Def, Schema, StructDef, TypeDef}, - utils::create_ident, + schema::{Def, EnumDef, FieldDef, Schema, StructDef, TypeDef, TypeId}, + utils::{create_ident, create_ident_tokens}, }; -use super::define_generator; +use super::{ + define_generator, + visit::{INLINE_LIMIT, Target, VisitorOutputs, generate_visit_type}, +}; -/// Generator for `ChildScopesCollector` visitor. +/// Generator for `ChildScopeCollector` visitor. pub struct ScopesCollectorGenerator; define_generator!(ScopesCollectorGenerator); impl Generator for ScopesCollectorGenerator { + fn prepare(&self, schema: &mut Schema, _codegen: &Codegen) { + ScopesCalculator::new(schema).calculate_all(); + } + fn generate(&self, schema: &Schema, _codegen: &Codegen) -> Output { - // Get `TypeDef` for `ScopeId` - let scope_id_type = schema.type_by_name("ScopeId").as_struct().unwrap(); + Output::Rust { + path: output_path(TRAVERSE_CRATE_PATH, "scopes_collector.rs"), + tokens: generate(schema), + } + } +} - let visit_methods = schema - .types - .iter() - .filter_map(|type_def| generate_for_type(type_def, scope_id_type, schema)); +/// State of calculation for whether a type contains a scope. +/// +/// As the AST is a cyclic graph, need to avoid infinite loops. +/// +/// * When start calculating for a type, state is set to `Calculating`. +/// * If can definitively determine whether type contains a scope, state is set to `Calculated`. +/// * `true` if struct has a scope itself, or any struct field / enum variant contains a scope. +/// * `false` if all struct fields / enum variants can be definitively determined not to contain a scope. +/// * If hit a circular reference, state is set back to `NotCalculated`. +/// It will be possible to determine definitively whether type contains a scope later on. +#[derive(Clone, Copy, PartialEq, Eq)] +enum CalculationState { + Calculated(bool), + NotCalculated, + Calculating, +} - let output = quote! { - use std::cell::Cell; +/// Calculator of which types contain scopes. +/// +/// `ScopesCalculator::new(schema).calculate_all()` calculates which types contain scopes, +/// and sets `def.visit.contains_scope = true` on [`StructDef`]s and [`EnumDef`]s which do. +struct ScopesCalculator<'s> { + calculation_states: IndexVec, + schema: &'s mut Schema, +} - ///@@line_break - use oxc_ast::ast::*; - use oxc_ast_visit::Visit; - use oxc_syntax::scope::{ScopeFlags, ScopeId}; +impl<'s> ScopesCalculator<'s> { + /// Create [`ScopesCalculator`]. + fn new(schema: &'s mut Schema) -> Self { + // Create bitset indexed by `TypeId` tracking the calculation state of each type + let calculation_states = IndexVec::::from_vec( + iter::repeat_n(CalculationState::NotCalculated, schema.types.len()).collect::>(), + ); - ///@@line_break - /// Visitor that locates all child scopes. - /// NB: Child scopes only, not grandchild scopes. - /// Does not do full traversal - stops each time it hits a node with a scope. - pub struct ChildScopeCollector { - pub(crate) scope_ids: Vec, + Self { calculation_states, schema } + } + + /// Calculate whether all types contain a scope or not. + fn calculate_all(&mut self) { + for type_id in self.schema.types.indices() { + let state = self.calculate_type(type_id); + if state == CalculationState::NotCalculated { + // No scope found for any of children, but hit a circular reference. + // As this was the starting point of traversal through AST graph, + // circular references can't contain a scope. So this type does not contain a scope. + self.calculation_states[type_id] = CalculationState::Calculated(false); } + } + } - ///@@line_break - impl ChildScopeCollector { - pub(crate) fn new() -> Self { - Self { scope_ids: vec![] } + /// Calculate if a type contains a scope (either its own, or in a child). + /// + /// Returns: + /// * `Calculated(true)` if definitely contains a scope. + /// * `Calculated(false)` if definitely does not contain a scope. + /// * `NotCalculated` if not possible to determine definitively due to circular dependency. + fn calculate_type(&mut self, type_id: TypeId) -> CalculationState { + match self.calculation_states[type_id] { + // Already calculated. Return result. + state @ CalculationState::Calculated(_) => return state, + // Currently calculating. Exit to avoid infinite loop. + CalculationState::Calculating => return CalculationState::NotCalculated, + // Not yet calculated. Calculate now. + CalculationState::NotCalculated => {} + } + + // Flag that currently calculating + self.calculation_states[type_id] = CalculationState::Calculating; + + let state = match &self.schema.types[type_id] { + TypeDef::Struct(_) => self.calculate_struct(type_id), + TypeDef::Enum(_) => self.calculate_enum(type_id), + // Primitives don't have scopes + TypeDef::Primitive(_) => CalculationState::Calculated(false), + // Containers contain a scope if their inner type does + TypeDef::Option(option_def) => self.calculate_type(option_def.inner_type_id), + TypeDef::Box(box_def) => self.calculate_type(box_def.inner_type_id), + TypeDef::Vec(vec_def) => self.calculate_type(vec_def.inner_type_id), + TypeDef::Cell(cell_def) => self.calculate_type(cell_def.inner_type_id), + }; + + // Either `Calculated(true)`, `Calculated(false)`, or `NotCalculated`. + // Note: Never `Calculating`. + self.calculation_states[type_id] = state; + + state + } + + /// Calculate if a struct contains a scope (either has its own scope, or one of fields does). + /// + /// Returns: + /// * `Calculated(true)` if definitely contains a scope. + /// * `Calculated(false)` if definitely does not contain a scope. + /// * `NotCalculated` if not possible to determine definitively due to circular dependency. + fn calculate_struct(&mut self, type_id: TypeId) -> CalculationState { + let struct_def = self.schema.struct_def(type_id); + + let state = if struct_def.visit.scope.is_some() { + CalculationState::Calculated(true) + } else { + // Check if any field contains a scope + let mut state = CalculationState::Calculated(false); + for field_index in struct_def.field_indices() { + let field_type_id = self.schema.struct_def(type_id).fields[field_index].type_id; + let field_state = self.calculate_type(field_type_id); + match field_state { + CalculationState::Calculated(true) => { + state = CalculationState::Calculated(true); + break; + } + CalculationState::NotCalculated => { + state = CalculationState::NotCalculated; + } + _ => {} + } + } + state + }; + + if state == CalculationState::Calculated(true) { + self.schema.struct_def_mut(type_id).visit.contains_scope = true; + } + + state + } + + /// Calculate if an enum contains a scope (one of its variants contains a scope). + /// + /// Returns: + /// * `Calculated(true)` if definitely contains a scope. + /// * `Calculated(false)` if definitely does not contain a scope. + /// * `NotCalculated` if not possible to determine definitively due to circular dependency. + fn calculate_enum(&mut self, type_id: TypeId) -> CalculationState { + let enum_def = self.schema.enum_def(type_id); + + let mut state = CalculationState::Calculated(false); + if !enum_def.is_fieldless() { + // Check if any variant contains a scope + for variant_index in enum_def.variant_indices() { + let variant = &self.schema.enum_def(type_id).variants[variant_index]; + if let Some(variant_type_id) = variant.field_type_id { + let variant_state = self.calculate_type(variant_type_id); + match variant_state { + CalculationState::Calculated(true) => { + state = CalculationState::Calculated(true); + break; + } + CalculationState::NotCalculated => { + state = CalculationState::NotCalculated; + } + _ => {} + } } + } - ///@@line_break - pub(crate) fn add_scope(&mut self, scope_id: &Cell>) { - self.scope_ids.push(scope_id.get().unwrap()); + // Check if any inherited enum contains a scope + if state != CalculationState::Calculated(true) { + for inherits_index in self.schema.enum_def(type_id).inherits_indices() { + let inherits_type_id = self.schema.enum_def(type_id).inherits[inherits_index]; + let inherits_state = self.calculate_type(inherits_type_id); + match inherits_state { + CalculationState::Calculated(true) => { + state = CalculationState::Calculated(true); + break; + } + CalculationState::NotCalculated => { + state = CalculationState::NotCalculated; + } + _ => {} + } } } + } + + if state == CalculationState::Calculated(true) { + self.schema.enum_def_mut(type_id).visit.contains_scope = true; + } + + state + } +} + +/// Generate `ChildScopeCollector`. +fn generate(schema: &Schema) -> TokenStream { + // Get `TypeId` for `ScopeId` + let scope_id_type_id = schema.type_names["ScopeId"]; + + let visit_methods = schema + .types + .iter() + .filter_map(|type_def| generate_visit_method_for_type(type_def, scope_id_type_id, schema)); + + quote! { + #![expect( + unused_variables, + clippy::semicolon_if_nothing_returned, + clippy::match_wildcard_for_single_variants, + clippy::match_same_arms, + clippy::single_match_else + )] + + ///@@line_break + use std::cell::Cell; + + ///@@line_break + use oxc_ast::ast::*; + use oxc_ast_visit::Visit; + use oxc_syntax::scope::{ScopeFlags, ScopeId}; + + ///@@line_break + /// Visitor that locates all child scopes. + /// + /// Note: Direct child scopes only, not grandchild scopes. + /// Does not do full traversal - stops each time it hits a node with a scope. + pub struct ChildScopeCollector { + pub(crate) scope_ids: Vec, + } + + ///@@line_break + impl ChildScopeCollector { + pub(crate) fn new() -> Self { + Self { scope_ids: vec![] } + } ///@@line_break - impl<'a> Visit<'a> for ChildScopeCollector { - #(#visit_methods)* + pub(crate) fn add_scope(&mut self, scope_id: &Cell>) { + self.scope_ids.push(scope_id.get().unwrap()); } - }; + } - Output::Rust { - path: output_path(TRAVERSE_CRATE_PATH, "scopes_collector.rs"), - tokens: output, + ///@@line_break + impl<'a> Visit<'a> for ChildScopeCollector { + #(#visit_methods)* } } } -fn generate_for_type( +/// [`VisitorOutputs`] for generating visitor calls for just `Visit` trait. +struct VisitOnly(TokenStream); + +impl VisitorOutputs for VisitOnly { + fn gen_each TokenStream>(f: F) -> Self { + Self(f(false)) + } + + fn map TokenStream>(self, f: F) -> Self { + Self(f(self.0, false)) + } +} + +/// Generate visitor method for a type. +/// +/// Returns `None` if no visitor method is required +/// (either the type is not visited, or the default `visit_*` method can be used). +fn generate_visit_method_for_type( type_def: &TypeDef, - scope_id_type: &StructDef, + scope_id_type_id: TypeId, + schema: &Schema, +) -> Option { + match type_def { + TypeDef::Struct(struct_def) => { + generate_visit_method_for_struct(struct_def, scope_id_type_id, schema) + } + TypeDef::Enum(enum_def) => generate_visit_method_for_enum(enum_def, schema), + _ => None, + } +} + +/// Generate visitor method for a struct. +/// +/// Returns `None` if no visitor method is required +/// (either the struct is not visited, or the default `visit_*` method can be used). +fn generate_visit_method_for_struct( + struct_def: &StructDef, + scope_id_type_id: TypeId, schema: &Schema, ) -> Option { - let struct_def = type_def.as_struct()?; + // Get visit method name. Exit if struct is not visited. + let visit_method_ident = struct_def.visit.visitor_ident()?; - // Find `ScopeId` field - let field = struct_def.fields.iter().find(|field| { - if let TypeDef::Cell(cell_def) = field.type_def(schema) { - if let TypeDef::Option(option_def) = cell_def.inner_type(schema) { - return option_def.inner_type_id == scope_id_type.id; + let (body, stmt_count) = if let Some(scope) = struct_def.visit.scope.as_ref() { + // Struct has its own scope. + // Visit fields which contain a scope before entering + after exiting this struct's scope. + let mut stmts_before = quote!(); + let mut stmts_after = quote!(); + let mut scope_id_field = None; + let mut stmt_count = 1; + + for (field_index, field) in struct_def.fields.iter().enumerate() { + // Identify `ScopeId` field + let field_type = field.type_def(schema); + if let TypeDef::Cell(cell_def) = field_type { + if let TypeDef::Option(option_def) = cell_def.inner_type(schema) { + if option_def.inner_type_id == scope_id_type_id { + scope_id_field = Some(field); + continue; + } + } + } + + // Check if field is before enter scope / after exit scope + let stmts = if field_index < scope.enter_before_index { + &mut stmts_before + } else if field_index >= scope.exit_before_index { + &mut stmts_after + } else { + continue; + }; + + if let Some(visit) = generate_visit_stmt_for_struct_field(field, schema) { + stmts.extend(visit); + stmt_count += 1; } } - false - })?; - // Generate visit method - let struct_ty = struct_def.ty(schema); - let field_ident = field.ident(); - let visit_method_ident = struct_def.visit.visitor_ident(); + let scope_id_field = scope_id_field.unwrap(); + let scope_id_field_ident = scope_id_field.ident(); + let body = quote! { + #stmts_before + self.add_scope(&it.#scope_id_field_ident); + #stmts_after + }; + (body, stmt_count) + } else if struct_def.visit.contains_scope { + // Struct does not have its own scope, but at least one of fields does. + // Only visit fields which contain a scope. + let mut body = quote!(); + let mut stmt_count = 0; + for field in &struct_def.fields { + if let Some(visit) = generate_visit_stmt_for_struct_field(field, schema) { + body.extend(visit); + stmt_count += 1; + } + } + + if stmt_count == struct_def.fields.len() { + // All fields visited. Use default visitor method. + return None; + } + + (body, stmt_count) + } else { + let body = quote! { + //!@ Struct does not contain a scope. Halt traversal. + }; + (body, 0) + }; + + // Generate visit method. + // `#[inline]` if there are `INLINE_LIMIT` or less statements. + let maybe_inline_attr = match stmt_count { + 0 => quote!( #[inline(always)] ), + _ if stmt_count <= INLINE_LIMIT => quote!( #[inline] ), + _ => quote!(), + }; + + let ty = struct_def.ty(schema); let extra_params = struct_def .visit @@ -102,9 +407,130 @@ fn generate_for_type( let visit_method = quote! { ///@@line_break - #[inline] - fn #visit_method_ident(&mut self, it: &#struct_ty #extra_params) { - self.add_scope(&it.#field_ident); + #maybe_inline_attr + fn #visit_method_ident(&mut self, it: &#ty #extra_params) { + #body + } + }; + Some(visit_method) +} + +/// Generate statement to visit a struct field if it contains a scope. +fn generate_visit_stmt_for_struct_field(field: &FieldDef, schema: &Schema) -> Option { + let field_type = field.type_def(schema); + let contains_scope = match field_type.innermost_type(schema) { + TypeDef::Struct(struct_def) => struct_def.visit.contains_scope, + TypeDef::Enum(enum_def) => enum_def.visit.contains_scope, + TypeDef::Primitive(_) => false, + _ => unreachable!(), + }; + if !contains_scope { + return None; + } + + // Generate visit statement for field + let field_ident = field.ident(); + generate_visit_type( + field_type, + &Target::Property(quote!( it.#field_ident )), + &field.visit.visit_args, + &field_ident, + "e!(self), + true, + schema, + ) + .map(|VisitOnly(visit)| visit) +} + +/// Generate visitor method for an enum. +/// +/// Returns `None` if no visitor method is required +/// (either the enum is not visited, or the default `visit_*` method can be used). +fn generate_visit_method_for_enum(enum_def: &EnumDef, schema: &Schema) -> Option { + // Get visit method name. Exit if enum is not visited. + let visit_method_ident = enum_def.visit.visitor_ident()?; + + let (body, maybe_inline_attr) = if enum_def.visit.contains_scope { + // Some variants contain scopes. Only visit variants which do. + let enum_ident = enum_def.ident(); + let mut unvisited_variants = quote!(); + let mut match_arm_count = 0; + let match_arms = enum_def + .all_variants(schema) + .filter_map(|variant| { + let variant_type = variant.field_type(schema)?; + let contains_scope = match variant_type.innermost_type(schema) { + TypeDef::Struct(struct_def) => struct_def.visit.contains_scope, + TypeDef::Enum(enum_def) => enum_def.visit.contains_scope, + _ => false, + }; + + let visit = if contains_scope { + generate_visit_type( + variant_type, + &Target::Reference(create_ident_tokens("it")), + &variant.visit.visit_args, + &create_ident_tokens("it"), + "e!(self), + false, + schema, + ) + .map(|VisitOnly(visit)| visit) + } else { + None + }; + + if let Some(visit) = visit { + match_arm_count += 1; + let variant_ident = variant.ident(); + Some(quote! { + #enum_ident::#variant_ident(it) => #visit, + }) + } else { + let doc = format!("@ `{}`", variant.name()); + unvisited_variants.extend(quote! { + #![doc = #doc] + }); + None + } + }) + .collect::(); + + // If all variants have scopes, no need for a custom visitor. + // The default one will visit all variants. + if unvisited_variants.is_empty() { + return None; + } + + let body = quote! { + match it { + #match_arms + _ => { + //!@ Remaining variants do not contain scopes: + #unvisited_variants + } + } + }; + // `#[inline]` if there are `INLINE_LIMIT` or less match arms + let maybe_inline_attr = + if match_arm_count <= INLINE_LIMIT { quote!( #[inline] ) } else { quote!() }; + (body, maybe_inline_attr) + } else { + // No variant contains a scope + let body = quote! { + //!@ Enum does not contain a scope. Halt traversal. + }; + let inline_attr = quote!( #[inline(always)] ); + (body, inline_attr) + }; + + // Generate visit method + let ty = enum_def.ty(schema); + let visit_method = quote! { + ///@@line_break + #maybe_inline_attr + fn #visit_method_ident(&mut self, it: &#ty) { + #body } }; Some(visit_method) diff --git a/tasks/ast_tools/src/generators/visit.rs b/tasks/ast_tools/src/generators/visit.rs index cd89bd539715c..109f72c54c391 100644 --- a/tasks/ast_tools/src/generators/visit.rs +++ b/tasks/ast_tools/src/generators/visit.rs @@ -18,8 +18,11 @@ use crate::{ use super::{AttrLocation, AttrPart, AttrPositions, attr_positions, define_generator}; /// Visitors with less than this number of fields/variants will be marked `#[inline]`. +/// +/// Also used by generator for `ChildScopeCollector` visitor. +// // TODO: Is this the ideal value? -const INLINE_LIMIT: usize = 5; +pub const INLINE_LIMIT: usize = 5; /// Generator for `Visit` and `VisitMut` traits. pub struct VisitGenerator; @@ -470,6 +473,7 @@ impl VisitBuilder<'_> { &Target::Property(quote!( it.#field_ident )), &field.visit.visit_args, &field_ident, + "e!(visitor), true, self.schema, )?; @@ -540,6 +544,7 @@ impl VisitBuilder<'_> { &Target::Reference(create_ident_tokens("it")), &variant.visit.visit_args, &create_ident_tokens("it"), + "e!(visitor), false, self.schema, )?; @@ -684,7 +689,8 @@ impl VisitBuilder<'_> { /// Trait for set of visitor call outputs. /// /// Implemented by `VisitAndVisitMut`. -trait VisitorOutputs { +/// Also used by generator for `ChildScopeCollector` visitor. +pub trait VisitorOutputs { /// Generate [`TokenStream`] for each output. /// /// Closure is passed `false` for `Visit` trait, `true` for `VisitMut`. @@ -699,7 +705,6 @@ trait VisitorOutputs { } /// [`VisitorOutputs`] for generating visitor calls for both `Visit` and `VisitMut` traits. -#[expect(unused)] // Linter is wrong. This type is constructed. struct VisitAndVisitMut { visit: TokenStream, visit_mut: TokenStream, @@ -735,24 +740,29 @@ impl VisitorOutputs for VisitAndVisitMut { /// * `field_ident` is [`Ident`] for the field. /// Is used in output for `Option`s. e.g. `span` in `if let Some(span) = ...`. /// +/// * `visitor` is identifier for the visitor - `visitor` in walk functions, `self` in visitor methods. +/// /// * `trailing_semicolon` indicates if a semicolon postfix is needed. /// This is `true` for struct fields, `false` for enum variants. /// +/// Also used by generator for `ChildScopeCollector` visitor. +/// /// [`Ident`]: struct@Ident -fn generate_visit_type( +pub fn generate_visit_type( type_def: &TypeDef, target: &Target, visit_args: &[(String, String)], field_ident: &TokenStream, + visitor: &TokenStream, trailing_semicolon: bool, schema: &Schema, ) -> Option { match type_def { TypeDef::Struct(_) | TypeDef::Enum(_) => { - generate_visit_struct_or_enum(type_def, target, visit_args, trailing_semicolon) + generate_visit_struct_or_enum(type_def, target, visit_args, visitor, trailing_semicolon) } TypeDef::Option(option_def) => { - generate_visit_option(option_def, target, visit_args, field_ident, schema) + generate_visit_option(option_def, target, visit_args, field_ident, visitor, schema) } TypeDef::Box(box_def) => { // `Box`es can be treated as transparent, as auto-deref handles it @@ -761,12 +771,13 @@ fn generate_visit_type( target, visit_args, field_ident, + visitor, trailing_semicolon, schema, ) } TypeDef::Vec(vec_def) => { - generate_visit_vec(vec_def, target, visit_args, trailing_semicolon, schema) + generate_visit_vec(vec_def, target, visit_args, visitor, trailing_semicolon, schema) } // Primitives and `Cell`s are not visited TypeDef::Primitive(_) | TypeDef::Cell(_) => None, @@ -784,6 +795,7 @@ fn generate_visit_struct_or_enum( type_def: &TypeDef, target: &Target, visit_args: &[(String, String)], + visitor: &TokenStream, trailing_semicolon: bool, ) -> Option { let visit_fn_ident = match type_def { @@ -792,7 +804,13 @@ fn generate_visit_struct_or_enum( _ => None?, }; - Some(generate_visit_with_visit_args(&visit_fn_ident, target, visit_args, trailing_semicolon)) + Some(generate_visit_with_visit_args( + &visit_fn_ident, + target, + visit_args, + visitor, + trailing_semicolon, + )) } /// Generate visitor calls with specified visitor function name. @@ -812,6 +830,7 @@ fn generate_visit_with_visit_args( visit_fn_ident: &Ident, target: &Target, visit_args: &[(String, String)], + visitor: &TokenStream, trailing_semicolon: bool, ) -> V { // Get extra function params for visit args. @@ -834,7 +853,7 @@ fn generate_visit_with_visit_args( V::gen_each(|is_mut| { let target_ref = target.generate_ref(is_mut); - let mut visit = quote!( visitor.#visit_fn_ident(#target_ref #extra_params) ); + let mut visit = quote!( #visitor.#visit_fn_ident(#target_ref #extra_params) ); if trailing_semicolon { visit.extend(quote!(;)); } @@ -870,6 +889,7 @@ fn generate_visit_option( target: &Target, visit_args: &[(String, String)], field_ident: &TokenStream, + visitor: &TokenStream, schema: &Schema, ) -> Option { let inner_type = option_def.inner_type(schema); @@ -878,6 +898,7 @@ fn generate_visit_option( &Target::Reference(field_ident.clone()), visit_args, field_ident, + visitor, true, schema, )?; @@ -921,6 +942,7 @@ fn generate_visit_vec( vec_def: &VecDef, target: &Target, visit_args: &[(String, String)], + visitor: &TokenStream, trailing_semicolon: bool, schema: &Schema, ) -> Option { @@ -930,6 +952,7 @@ fn generate_visit_vec( &visit_fn_ident, target, visit_args, + visitor, trailing_semicolon, )); } @@ -958,6 +981,7 @@ fn generate_visit_vec( &Target::Reference(create_ident_tokens("el")), visit_args, &create_ident_tokens("it"), + visitor, true, schema, )?; @@ -982,7 +1006,9 @@ fn generate_visit_vec( /// /// * `Target::Property` represents an object property e.g. `it.span`. /// Needs `&` / `&mut` prepended to it when using it in most circumstances. -enum Target { +/// +/// Also used by generator for `ChildScopeCollector` visitor. +pub enum Target { Reference(TokenStream), Property(TokenStream), } diff --git a/tasks/ast_tools/src/main.rs b/tasks/ast_tools/src/main.rs index 41e4539b5bd8c..40d0cddab4f89 100644 --- a/tasks/ast_tools/src/main.rs +++ b/tasks/ast_tools/src/main.rs @@ -266,9 +266,9 @@ const GENERATORS: &[&(dyn Generator + Sync)] = &[ &generators::AssertLayouts, &generators::AstKindGenerator, &generators::AstBuilderGenerator, - &generators::ScopesCollectorGenerator, &generators::GetIdGenerator, &generators::VisitGenerator, + &generators::ScopesCollectorGenerator, &generators::Utf8ToUtf16ConverterGenerator, &generators::RawTransferGenerator, &generators::TypescriptGenerator, diff --git a/tasks/ast_tools/src/schema/extensions/visit.rs b/tasks/ast_tools/src/schema/extensions/visit.rs index 451f27690159b..06e98011b2d7e 100644 --- a/tasks/ast_tools/src/schema/extensions/visit.rs +++ b/tasks/ast_tools/src/schema/extensions/visit.rs @@ -10,6 +10,8 @@ pub struct VisitStruct { pub visitor_names: Option, pub visit_args: Vec<(String, String)>, pub scope: Option, + /// `true` if this type has a scope, or any of its fields contain a scope. + pub contains_scope: bool, } impl VisitStruct { @@ -32,6 +34,8 @@ pub struct VisitEnum { /// Name of `visit_*` method and `walk_*` function. /// `None` if this enum is not visited. pub visitor_names: Option, + /// `true` if any variants contain a scope. + pub contains_scope: bool, } impl VisitEnum {