use llmcc_core::context::CompileUnit;
use llmcc_core::ir::{HirId, HirIdent, HirNode, HirScope};
use llmcc_core::next_hir_id;
use llmcc_core::scope::{Scope, ScopeStack};
use llmcc_core::symbol::{ScopeId, SymKind, SymKindSet, Symbol};
use llmcc_resolver::{CollectorScopes, ResolverOption};
use std::collections::HashMap;
use crate::LangRust;
use crate::token::AstVisitorRust;
fn is_method_context(parent: Option<&Symbol>) -> bool {
parent.is_some_and(|p| {
matches!(
p.kind(),
SymKind::Struct | SymKind::Enum | SymKind::Trait | SymKind::UnresolvedType
)
})
}
type ScopeEntryCallback<'tcx> = Box<dyn FnOnce(&HirNode<'tcx>, &mut CollectorScopes<'tcx>) + 'tcx>;
#[derive(Debug)]
pub struct CollectorVisitor<'tcx> {
scope_map: HashMap<ScopeId, &'tcx Scope<'tcx>>,
}
impl<'tcx> CollectorVisitor<'tcx> {
fn new() -> Self {
Self {
scope_map: HashMap::new(),
}
}
fn declare_symbol(
&self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &CollectorScopes<'tcx>,
kind: SymKind,
field_id: u16,
) -> Option<&'tcx Symbol> {
let ident = node
.ident_by_field(unit, field_id)
.or_else(|| node.as_scope().and_then(|sn| sn.opt_ident()))?;
tracing::trace!("declaring symbol '{}' of kind {:?}", ident.name, kind);
let sym = scopes.lookup_or_insert(ident.name, node, kind)?;
ident.set_symbol(sym);
if let Some(sn) = node.as_scope() {
sn.set_ident(ident);
}
Some(sym)
}
fn collect_pattern_identifiers(
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &CollectorScopes<'tcx>,
kind: SymKind,
) -> Vec<&'tcx Symbol> {
let mut symbols = Vec::new();
Self::collect_pattern_identifiers_impl(unit, node, scopes, kind, &mut symbols);
symbols
}
fn collect_pattern_identifiers_impl(
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &CollectorScopes<'tcx>,
kind: SymKind,
symbols: &mut Vec<&'tcx Symbol>,
) {
if matches!(
node.kind_id(),
LangRust::type_identifier | LangRust::primitive_type | LangRust::field_identifier |
LangRust::scoped_identifier | LangRust::scoped_type_identifier
) {
return;
}
if let Some(ident) = node.as_ident() {
let name = ident.name.to_string();
let sym = scopes.lookup_or_insert(&name, node, kind);
if let Some(sym) = sym {
ident.set_symbol(sym);
symbols.push(sym);
}
}
for &child_id in node.child_ids() {
let child = unit.hir_node(child_id);
Self::collect_pattern_identifiers_impl(unit, &child, scopes, kind, symbols);
}
}
fn alloc_scope(&mut self, unit: &CompileUnit<'tcx>, symbol: &'tcx Symbol) -> &'tcx Scope<'tcx> {
let scope = unit.cc.alloc_scope(symbol.owner());
scope.set_symbol(symbol);
self.scope_map.insert(scope.id(), scope);
scope
}
fn get_scope(&self, scope_id: ScopeId) -> Option<&'tcx Scope<'tcx>> {
self.scope_map.get(&scope_id).copied()
}
fn lookup_or_convert(
&mut self,
unit: &CompileUnit<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
name: &str,
node: &HirNode<'tcx>,
kind: SymKind,
) -> Option<&'tcx Symbol> {
tracing::trace!(
"looking up or converting symbol '{}' of kind {:?}",
name,
kind
);
if let Some(symbol) = scopes.lookup_symbol(name, SymKindSet::from_kind(kind)) {
tracing::trace!(
"found existing symbol '{}' of kind {:?} {:?}",
name,
kind,
symbol,
);
return Some(symbol);
}
if let Some(symbol) =
scopes.lookup_symbol(name, SymKindSet::from_kind(SymKind::UnresolvedType))
{
tracing::trace!(
"found existing unresolved symbol '{}', converting to {:?}, {:?}",
name,
kind,
symbol,
);
symbol.set_kind(kind);
return Some(symbol);
}
if let Some(symbol) = scopes.lookup_or_insert(name, node, kind) {
tracing::trace!("inserting new symbol '{}' of kind {:?}", name, kind,);
if symbol.opt_scope().is_none() {
let scope = self.alloc_scope(unit, symbol);
symbol.set_scope(scope.id());
}
return Some(symbol);
}
None
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all)]
fn visit_with_scope(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
sym: &'tcx Symbol,
sn: &'tcx HirScope<'tcx>,
ident: &'tcx HirIdent<'tcx>,
on_scope_enter: Option<ScopeEntryCallback<'tcx>>,
) {
tracing::trace!(
"visiting with scope for symbol {}",
sym.format(Some(scopes.interner()))
);
ident.set_symbol(sym);
sn.set_ident(ident);
let scope = if sym.opt_scope().is_none() {
tracing::trace!(
"allocating new scope for symbol {}",
sym.format(Some(scopes.interner()))
);
self.alloc_scope(unit, sym)
} else {
tracing::trace!(
"use existing scope for symbol {}",
sym.format(Some(scopes.interner()))
);
match self.get_scope(sym.scope()) {
Some(s) => s,
None => {
tracing::warn!(
"scope not found for symbol {}, allocating new",
sym.format(Some(scopes.interner()))
);
self.alloc_scope(unit, sym)
}
}
};
sym.set_scope(scope.id());
sn.set_scope(scope);
scopes.push_scope(scope);
if let Some(callback) = on_scope_enter {
callback(node, scopes);
}
self.visit_children(unit, node, scopes, scope, Some(sym));
scopes.pop_scope();
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all)]
fn visit_scoped_named(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
_namespace: &'tcx Scope<'tcx>,
_parent: Option<&Symbol>,
kind: SymKind,
field_id: u16,
on_scope_enter: Option<ScopeEntryCallback<'tcx>>,
) {
if let Some((sn, ident)) = node.scope_and_ident_by_field(unit, field_id) {
tracing::trace!(
"visiting scoped named node with kind '{:?}' '{}'",
kind,
ident.name
);
if let Some(sym) = self.lookup_or_convert(unit, scopes, ident.name, node, kind) {
self.visit_with_scope(unit, node, scopes, sym, sn, ident, on_scope_enter);
}
} else {
tracing::warn!("scoped named node is missing scope or ident, skipping");
}
}
}
impl<'tcx> AstVisitorRust<'tcx, CollectorScopes<'tcx>> for CollectorVisitor<'tcx> {
#[tracing::instrument(skip_all)]
fn visit_block(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
_namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
if let Some(sn) = node.as_scope() {
let scope = unit.cc.alloc_scope(node.id());
sn.set_scope(scope);
scopes.push_scope(scope);
self.visit_children(unit, node, scopes, scope, parent);
scopes.pop_scope();
}
}
fn visit_match_block(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_block(unit, node, scopes, namespace, parent);
}
#[rustfmt::skip]
#[tracing::instrument(skip_all)]
fn visit_source_file(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let start_depth = scopes.scope_depth();
let meta = unit.unit_meta();
let mut crate_scope: Option<&'tcx Scope<'tcx>> = None;
if let Some(ref crate_name) = meta.package_name
&& let Some(symbol) = scopes.lookup_or_insert_global(crate_name, node, SymKind::Crate)
{
tracing::trace!("insert crate symbol in globals '{}'", crate_name);
scopes.push_scope_with(node, Some(symbol));
crate_scope = scopes.top();
}
let mut module_wrapper_scope: Option<&'tcx Scope<'tcx>> = None;
if let Some(ref module_name) = meta.module_name
&& let Some(module_sym) = scopes.lookup_or_insert_global(module_name, node, SymKind::Module)
{
tracing::trace!("insert module symbol in globals '{}'", module_name);
let mod_scope = self.alloc_scope(unit, module_sym);
if let Some(crate_s) = crate_scope {
mod_scope.add_parent(crate_s);
}
module_wrapper_scope = Some(mod_scope);
}
let sn = node.as_scope().unwrap();
if let Some(ref file_name) = meta.file_name
&& let Some(file_sym) = scopes.lookup_or_insert(file_name, node, SymKind::File)
{
tracing::trace!("insert file symbol '{}' in current scope", file_name);
let arena_name = unit.cc.arena().alloc_str(file_name);
let ident = unit.cc.alloc_file_ident(next_hir_id(), arena_name, file_sym);
ident.set_symbol(file_sym);
sn.set_ident(ident);
let scope = self.alloc_scope(unit, file_sym);
file_sym.set_scope(scope.id());
sn.set_scope(scope);
if let Some(crate_s) = crate_scope {
scope.add_parent(crate_s);
}
if let Some(module_s) = module_wrapper_scope {
scope.add_parent(module_s);
}
scopes.push_scope(scope);
if let Some(crate_sym) = scopes.lookup_or_insert_global("crate", node, SymKind::Module) {
tracing::trace!("insert 'crate' symbol in globals");
crate_sym.set_scope(scopes.scopes().globals().id());
}
if file_name != "lib" && file_name != "main" && module_wrapper_scope.is_none() {
if let Some(mod_sym) = scopes.insert_in_global(file_name, node, SymKind::Module) {
tracing::trace!("link file '{}' as module in global scope", file_name);
mod_sym.set_scope(scope.id());
}
if let Some(crate_s) = crate_scope
&& let Some(mod_sym) =
scopes.insert_in_scope(crate_s, file_name, node, SymKind::Module)
{
tracing::trace!(
"link file '{}' as module in crate scope {:?}",
file_name,
crate_s.id()
);
mod_sym.set_scope(scope.id());
}
}
}
self.visit_children(unit, node, scopes, namespace, parent);
scopes.pop_until(start_depth);
}
#[tracing::instrument(skip_all)]
fn visit_mod_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
if node.child_by_field(unit, LangRust::field_body).is_none() {
return;
}
let parent_scope_id = scopes.top().map(|s| s.id()).unwrap_or(namespace.id());
let on_scope_enter: ScopeEntryCallback<'tcx> = Box::new(move |node, scopes| {
if let Some(super_sym) = scopes.lookup_or_insert("super", node, SymKind::Module) {
super_sym.set_scope(parent_scope_id);
tracing::trace!(
"inserted 'super' symbol pointing to scope {:?}",
parent_scope_id
);
}
});
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
SymKind::Namespace,
LangRust::field_name,
Some(on_scope_enter),
);
}
#[tracing::instrument(skip_all)]
fn visit_function_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let is_method = is_method_context(parent);
let kind = if is_method {
SymKind::Method
} else {
SymKind::Function
};
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
kind,
LangRust::field_name,
None,
);
if !is_method
&& let Some((_, ident)) = node.scope_and_ident_by_field(unit, LangRust::field_name)
&& let Some(sym) = scopes.lookup_symbol(ident.name, SymKindSet::from_kind(kind))
{
scopes.globals().insert(sym);
}
}
#[tracing::instrument(skip_all)]
fn visit_function_signature_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let kind = if is_method_context(parent) {
SymKind::Method
} else {
SymKind::Function
};
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
kind,
LangRust::field_name,
None,
);
}
#[tracing::instrument(skip_all)]
fn visit_struct_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
SymKind::Struct,
LangRust::field_name,
Some(Box::new(|node, scopes| {
let _ = scopes.lookup_or_insert("self", node, SymKind::TypeAlias);
let _ = scopes.lookup_or_insert("Self", node, SymKind::TypeAlias);
})),
);
if let Some((_, ident)) = node.scope_and_ident_by_field(unit, LangRust::field_name)
&& let Some(sym) =
scopes.lookup_symbol(ident.name, SymKindSet::from_kind(SymKind::Struct))
{
scopes.globals().insert(sym);
}
}
#[tracing::instrument(skip_all)]
fn visit_enum_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
SymKind::Enum,
LangRust::field_name,
None,
);
if let Some((_, ident)) = node.scope_and_ident_by_field(unit, LangRust::field_name)
&& let Some(sym) =
scopes.lookup_symbol(ident.name, SymKindSet::from_kind(SymKind::Enum))
{
scopes.globals().insert(sym);
}
}
#[tracing::instrument(skip_all)]
fn visit_trait_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
SymKind::Trait,
LangRust::field_name,
Some(Box::new(|node, scopes| {
let _ = scopes.lookup_or_insert("self", node, SymKind::TypeAlias);
let _ = scopes.lookup_or_insert("Self", node, SymKind::TypeAlias);
})),
);
if let Some((_, ident)) = node.scope_and_ident_by_field(unit, LangRust::field_name)
&& let Some(sym) =
scopes.lookup_symbol(ident.name, SymKindSet::from_kind(SymKind::Trait))
{
scopes.globals().insert(sym);
}
}
#[tracing::instrument(skip_all)]
fn visit_impl_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
_namespace: &'tcx Scope<'tcx>,
_parent: Option<&Symbol>,
) {
if let Some(ti) = node.ident_by_field(unit, LangRust::field_trait) {
let symbol = scopes
.lookup_symbol(ti.name, SymKindSet::from_kind(SymKind::Trait))
.or_else(|| {
scopes.lookup_symbol(ti.name, SymKindSet::from_kind(SymKind::UnresolvedType))
})
.or_else(|| scopes.lookup_or_insert(ti.name, node, SymKind::UnresolvedType));
if let Some(symbol) = symbol {
ti.set_symbol(symbol);
}
}
if let Some((sn, ti)) = node.scope_and_ident_by_field(unit, LangRust::field_type)
&& let Some(symbol) =
self.lookup_or_convert(unit, scopes, ti.name, node, SymKind::UnresolvedType)
{
tracing::trace!(
"visiting impl for type '{}', {:?}",
ti.name,
symbol.format(Some(scopes.interner())),
);
ti.set_symbol(symbol);
self.visit_with_scope(unit, node, scopes, symbol, sn, ti, None);
}
}
#[tracing::instrument(skip_all)]
fn visit_macro_definition(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
SymKind::Macro,
LangRust::field_name,
None,
);
}
#[tracing::instrument(skip_all)]
fn visit_const_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
_parent: Option<&Symbol>,
) {
if let Some(symbol) =
self.declare_symbol(unit, node, scopes, SymKind::Const, LangRust::field_name)
{
self.visit_children(unit, node, scopes, namespace, Some(symbol));
}
}
#[tracing::instrument(skip_all)]
fn visit_static_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
_parent: Option<&Symbol>,
) {
if let Some(symbol) =
self.declare_symbol(unit, node, scopes, SymKind::Static, LangRust::field_name)
{
self.visit_children(unit, node, scopes, namespace, Some(symbol));
}
}
#[tracing::instrument(skip_all)]
fn visit_type_item(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
_parent: Option<&Symbol>,
) {
if let Some(symbol) =
self.declare_symbol(unit, node, scopes, SymKind::TypeAlias, LangRust::field_name)
{
self.visit_children(unit, node, scopes, namespace, Some(symbol));
}
}
#[tracing::instrument(skip_all)]
fn visit_type_parameter(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let _ = self.declare_symbol(
unit,
node,
scopes,
SymKind::TypeParameter,
LangRust::field_name,
);
self.visit_children(unit, node, scopes, namespace, parent);
}
#[tracing::instrument(skip_all)]
fn visit_const_parameter(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let _ = self.declare_symbol(unit, node, scopes, SymKind::Const, LangRust::field_name);
self.visit_children(unit, node, scopes, namespace, parent);
}
#[tracing::instrument(skip_all)]
fn visit_associated_type(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let _ = self.declare_symbol(unit, node, scopes, SymKind::TypeAlias, LangRust::field_name);
self.visit_children(unit, node, scopes, namespace, parent);
}
#[tracing::instrument(skip_all)]
fn visit_where_predicate(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_children(unit, node, scopes, namespace, parent);
}
#[tracing::instrument(skip_all)]
fn visit_array_type(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let _ = self.declare_symbol(
unit,
node,
scopes,
SymKind::CompositeType,
LangRust::field_name,
);
self.visit_children(unit, node, scopes, namespace, parent);
}
#[tracing::instrument(skip_all)]
fn visit_tuple_type(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_array_type(unit, node, scopes, namespace, parent);
}
#[tracing::instrument(skip_all)]
fn visit_primitive_type(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_children(unit, node, scopes, namespace, parent);
}
fn visit_abstract_type(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
self.visit_children(unit, node, scopes, namespace, parent);
}
fn visit_field_declaration(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let _ = self.declare_symbol(unit, node, scopes, SymKind::Field, LangRust::field_name);
self.visit_children(unit, node, scopes, namespace, parent);
}
fn visit_enum_variant(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let parent_enum = parent.or_else(|| namespace.opt_symbol());
self.visit_scoped_named(
unit,
node,
scopes,
namespace,
parent,
SymKind::EnumVariant,
LangRust::field_name,
None,
);
if let Some(enum_sym) = parent_enum
&& let Some(ident) = node.ident_by_field(unit, LangRust::field_name)
&& let Some(variant_sym) =
scopes.lookup_symbol(ident.name, SymKindSet::from_kind(SymKind::EnumVariant))
{
variant_sym.set_type_of(enum_sym.id);
}
}
fn visit_parameter(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
_parent: Option<&Symbol>,
) {
if let Some(ident) = node.ident_by_field(unit, LangRust::field_pattern)
&& ident.name == "self"
&& let Some(symbol) =
scopes.lookup_symbol(ident.name, SymKindSet::from_kind(SymKind::Field))
{
ident.set_symbol(symbol);
self.visit_children(unit, node, scopes, namespace, Some(symbol));
return;
}
if let Some(pattern) = node.child_by_field(unit, LangRust::field_pattern) {
if pattern.as_ident().is_some() {
if let Some(symbol) = self.declare_symbol(
unit,
node,
scopes,
SymKind::Variable,
LangRust::field_pattern,
) {
self.visit_children(unit, node, scopes, namespace, Some(symbol));
return;
}
} else {
let _ =
Self::collect_pattern_identifiers(unit, &pattern, scopes, SymKind::Variable);
self.visit_children(unit, node, scopes, namespace, None);
return;
}
}
if let Some(symbol) = self.declare_symbol(
unit,
node,
scopes,
SymKind::Variable,
LangRust::field_pattern,
) {
self.visit_children(unit, node, scopes, namespace, Some(symbol));
}
}
fn visit_closure_expression(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
if let Some(sn) = node.as_scope() {
let scope = unit.cc.alloc_scope(node.id());
sn.set_scope(scope);
scope.add_parent(namespace);
scopes.push_scope(scope);
if let Some(params) = node.child_by_field(unit, LangRust::field_parameters) {
let _ = Self::collect_pattern_identifiers(unit, ¶ms, scopes, SymKind::Variable);
}
self.visit_children(unit, node, scopes, scope, parent);
scopes.pop_scope();
}
}
fn visit_let_declaration(
&mut self,
unit: &CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scopes: &mut CollectorScopes<'tcx>,
namespace: &'tcx Scope<'tcx>,
parent: Option<&Symbol>,
) {
let is_closure = node
.child_by_field(unit, LangRust::field_value)
.map(|v| v.kind_id() == LangRust::closure_expression)
.unwrap_or(false);
let kind = if is_closure {
SymKind::Closure
} else {
SymKind::Variable
};
let let_syms = if let Some(pattern) = node.child_by_field(unit, LangRust::field_pattern) {
Self::collect_pattern_identifiers(unit, &pattern, scopes, kind)
} else {
vec![]
};
if is_closure && !let_syms.is_empty() {
self.visit_children(unit, node, scopes, namespace, Some(let_syms[0]));
} else {
self.visit_children(unit, node, scopes, namespace, parent);
}
}
}
pub fn collect_symbols<'tcx>(
unit: CompileUnit<'tcx>,
node: &HirNode<'tcx>,
scope_stack: ScopeStack<'tcx>,
_config: &ResolverOption,
) -> &'tcx Scope<'tcx> {
let cc = unit.cc;
let arena = cc.arena();
let unit_globals_val = Scope::new(HirId(unit.index));
let scope_id = unit_globals_val.id().0;
let unit_globals = arena.alloc_with_id(scope_id, unit_globals_val);
let mut scopes = CollectorScopes::new(cc, unit.index, scope_stack, unit_globals);
let mut visit = CollectorVisitor::new();
visit.visit_node(&unit, node, &mut scopes, unit_globals, None);
unit_globals
}