//! This module implements import-resolution/macro expansion algorithm. //! //! The result of this module is `DefMap`: a data structure which contains: //! //! * a tree of modules for the crate //! * for each module, a set of items visible in the module (directly declared //! or imported) //! //! Note that `DefMap` contains fully macro expanded code. //! //! Computing `DefMap` can be partitioned into several logically //! independent "phases". The phases are mutually recursive though, there's no //! strict ordering. //! //! ## Collecting RawItems //! //! This happens in the `raw` module, which parses a single source file into a //! set of top-level items. Nested imports are desugared to flat imports in this //! phase. Macro calls are represented as a triple of `(Path, Option, //! TokenTree)`. //! //! ## Collecting Modules //! //! This happens in the `collector` module. In this phase, we recursively walk //! tree of modules, collect raw items from submodules, populate module scopes //! with defined items (so, we assign item ids in this phase) and record the set //! of unresolved imports and macros. //! //! While we walk tree of modules, we also record macro_rules definitions and //! expand calls to macro_rules defined macros. //! //! ## Resolving Imports //! //! We maintain a list of currently unresolved imports. On every iteration, we //! try to resolve some imports from this list. If the import is resolved, we //! record it, by adding an item to current module scope and, if necessary, by //! recursively populating glob imports. //! //! ## Resolving Macros //! //! macro_rules from the same crate use a global mutable namespace. We expand //! them immediately, when we collect modules. //! //! Macros from other crates (including proc-macros) can be used with //! `foo::bar!` syntax. We handle them similarly to imports. There's a list of //! unexpanded macros. On every iteration, we try to resolve each macro call //! path and, upon success, we run macro expansion and "collect module" phase on //! the result pub mod assoc; pub mod attr_resolution; mod collector; pub mod diagnostics; mod mod_resolution; mod path_resolution; pub mod proc_macro; #[cfg(test)] mod tests; use std::ops::{Deref, DerefMut, Index, IndexMut}; use base_db::Crate; use either::Either; use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, }; use intern::Symbol; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{AstNode, SmolStr, SyntaxNode, ToSmolStr, ast}; use triomphe::Arc; use tt::TextRange; use crate::{ AstId, BlockId, BlockLoc, BuiltinDeriveImplId, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles, MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, db::DefDatabase, item_scope::{BuiltinShadowMode, ItemScope}, item_tree::TreeId, nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, per_ns::PerNs, visibility::{Visibility, VisibilityExplicitness}, }; pub use self::path_resolution::ResolvePathResultPrefixInfo; #[cfg(test)] thread_local! { /// HACK: In order to test builtin derive expansion, we gate their fast path with this atomic when cfg(test). pub(crate) static ENABLE_BUILTIN_DERIVE_FAST_PATH: std::cell::Cell = const { std::cell::Cell::new(true) }; } #[inline] #[cfg(test)] fn enable_builtin_derive_fast_path() -> bool { ENABLE_BUILTIN_DERIVE_FAST_PATH.get() } #[inline(always)] #[cfg(not(test))] fn enable_builtin_derive_fast_path() -> bool { true } const PREDEFINED_TOOLS: &[SmolStr] = &[ SmolStr::new_static("clippy"), SmolStr::new_static("rustfmt"), SmolStr::new_static("diagnostic"), SmolStr::new_static("miri"), SmolStr::new_static("rust_analyzer"), ]; /// Parts of the def map that are only needed when analyzing code in the same crate. /// /// There are some data in the def map (e.g. extern prelude) that is only needed when analyzing /// things in the same crate (and maybe in the IDE layer), e.g. the extern prelude. If we put /// it in the DefMap dependant DefMaps will be invalidated when they change (e.g. when we add /// a dependency to the crate). Instead we split them out of the DefMap into a LocalDefMap struct. /// `crate_local_def_map()` returns both, and `crate_def_map()` returns only the external-relevant /// DefMap. #[derive(Debug, PartialEq, Eq, Default)] pub struct LocalDefMap { // FIXME: There are probably some other things that could be here, but this is less severe and you // need to be careful with things that block def maps also have. /// The extern prelude which contains all root modules of external crates that are in scope. extern_prelude: FxIndexMap)>, } impl std::hash::Hash for LocalDefMap { fn hash(&self, state: &mut H) { let LocalDefMap { extern_prelude } = self; extern_prelude.len().hash(state); for (name, (crate_root, extern_crate)) in extern_prelude { name.hash(state); crate_root.hash(state); extern_crate.hash(state); } } } impl LocalDefMap { pub(crate) const EMPTY: &Self = &Self { extern_prelude: FxIndexMap::with_hasher(rustc_hash::FxBuildHasher) }; fn shrink_to_fit(&mut self) { let Self { extern_prelude } = self; extern_prelude.shrink_to_fit(); } pub(crate) fn extern_prelude( &self, ) -> impl DoubleEndedIterator))> + '_ { self.extern_prelude.iter().map(|(name, &def)| (name, def)) } } /// Contains the results of (early) name resolution. /// /// A `DefMap` stores the module tree and the definitions that are in scope in every module after /// item-level macros have been expanded. /// /// Every crate has a primary `DefMap` whose root is the crate's main file (`main.rs`/`lib.rs`), /// computed by the `crate_def_map` query. Additionally, every block expression introduces the /// opportunity to write arbitrary item and module hierarchies, and thus gets its own `DefMap` that /// is computed by the `block_def_map` query. #[derive(Debug, PartialEq, Eq)] pub struct DefMap { /// The crate this `DefMap` belongs to. krate: Crate, /// When this is a block def map, this will hold the block id of the block and module that /// contains this block. block: Option, pub root: ModuleId, /// The modules and their data declared in this crate. pub modules: ModulesMap, /// The prelude module for this crate. This either comes from an import /// marked with the `prelude_import` attribute, or (in the normal case) from /// a dependency (`std` or `core`). /// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used, /// but that attribute is nightly and when used in a block, it affects resolution globally /// so we aren't handling this correctly anyways). prelude: Option<(ModuleId, Option)>, /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that /// this contains all kinds of macro, not just `macro_rules!` macro. /// ExternCrateId being None implies it being imported from the general prelude import. macro_use_prelude: FxHashMap)>, /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. // FIXME: Figure out a better way for the IDE layer to resolve these? derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, Either)>>, /// A mapping from [`hir_expand::MacroDefId`] to [`crate::MacroId`]. pub macro_def_to_macro_id: FxHashMap, /// The diagnostics that need to be emitted for this crate. diagnostics: Vec, /// The crate data that is shared between a crate's def map and all its block def maps. data: Arc, } /// Data that belongs to a crate which is shared between a crate's def map and all its block def maps. #[derive(Clone, Debug, PartialEq, Eq)] struct DefMapCrateData { /// Side table for resolving derive helpers. exported_derives: FxHashMap>, fn_proc_macro_mapping: FxHashMap, fn_proc_macro_mapping_back: FxHashMap, /// Custom tool modules registered with `#![register_tool]`. registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. unstable_features: FxHashSet, /// `#[rustc_coherence_is_core]` rustc_coherence_is_core: bool, no_core: bool, no_std: bool, edition: Edition, recursion_limit: Option, } impl DefMapCrateData { fn new(edition: Edition) -> Self { Self { exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), fn_proc_macro_mapping_back: FxHashMap::default(), registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), unstable_features: FxHashSet::default(), rustc_coherence_is_core: false, no_core: false, no_std: false, edition, recursion_limit: None, } } fn shrink_to_fit(&mut self) { let Self { exported_derives, fn_proc_macro_mapping, fn_proc_macro_mapping_back, registered_tools, unstable_features, rustc_coherence_is_core: _, no_core: _, no_std: _, edition: _, recursion_limit: _, } = self; exported_derives.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); fn_proc_macro_mapping_back.shrink_to_fit(); registered_tools.shrink_to_fit(); unstable_features.shrink_to_fit(); } } /// For `DefMap`s computed for a block expression, this stores its location in the parent map. #[derive(Debug, PartialEq, Eq, Clone, Copy)] struct BlockInfo { /// The `BlockId` this `DefMap` was created from. block: BlockId, /// The containing module. parent: ModuleId, } impl std::ops::Index for DefMap { type Output = ModuleData; fn index(&self, id: ModuleId) -> &ModuleData { self.modules .get(&id) .unwrap_or_else(|| panic!("ModuleId not found in ModulesMap {:#?}: {id:#?}", self.root)) } } impl std::ops::IndexMut for DefMap { fn index_mut(&mut self, id: ModuleId) -> &mut ModuleData { &mut self.modules[id] } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum ModuleOrigin { CrateRoot { definition: EditionedFileId, }, /// Note that non-inline modules, by definition, live inside non-macro file. File { is_mod_rs: bool, declaration: FileAstId, declaration_tree_id: TreeId, definition: EditionedFileId, }, Inline { definition_tree_id: TreeId, definition: FileAstId, }, /// Pseudo-module introduced by a block scope (contains only inner items). BlockExpr { id: BlockId, block: AstId, }, } impl ModuleOrigin { pub fn declaration(&self) -> Option> { match self { &ModuleOrigin::File { declaration, declaration_tree_id, .. } => { Some(AstId::new(declaration_tree_id.file_id(), declaration)) } &ModuleOrigin::Inline { definition, definition_tree_id } => { Some(AstId::new(definition_tree_id.file_id(), definition)) } ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None, } } pub fn file_id(&self) -> Option { match self { ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { Some(*definition) } _ => None, } } pub fn is_inline(&self) -> bool { match self { ModuleOrigin::Inline { .. } | ModuleOrigin::BlockExpr { .. } => true, ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false, } } /// Returns a node which defines this module. /// That is, a file or a `mod foo {}` with items. pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile { match self { &ModuleOrigin::File { definition: editioned_file_id, .. } | &ModuleOrigin::CrateRoot { definition: editioned_file_id } => { let sf = db.parse(editioned_file_id).tree(); InFile::new(editioned_file_id.into(), ModuleSource::SourceFile(sf)) } &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( definition_tree_id.file_id(), ModuleSource::Module( AstId::new(definition_tree_id.file_id(), definition).to_node(db), ), ), ModuleOrigin::BlockExpr { block, .. } => { InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db))) } } } } #[derive(Debug, PartialEq, Eq)] pub struct ModuleData { /// Where does this module come from? pub origin: ModuleOrigin, /// Declared visibility of this module. pub visibility: Visibility, /// Parent module in the same `DefMap`. /// /// [`None`] for block modules because they are always its `DefMap`'s root. pub parent: Option, pub children: FxIndexMap, pub scope: ItemScope, } #[inline] pub fn crate_def_map(db: &dyn DefDatabase, crate_id: Crate) -> &DefMap { crate_local_def_map(db, crate_id).def_map(db) } #[salsa_macros::tracked] pub(crate) struct DefMapPair<'db> { #[tracked] #[returns(ref)] pub(crate) def_map: DefMap, #[returns(ref)] pub(crate) local: LocalDefMap, } #[salsa_macros::tracked(returns(ref))] pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> { let krate = crate_id.data(db); let _p = tracing::info_span!( "crate_def_map_query", name=?crate_id .extra_data(db) .display_name .as_ref() .map(|it| it.crate_name().to_smolstr()) .unwrap_or_default() ) .entered(); let root_file_id = crate_id.root_file_id(db); let module_data = ModuleData::new( ModuleOrigin::CrateRoot { definition: root_file_id }, Visibility::Public, None, ); let def_map = DefMap::empty( db, crate_id, Arc::new(DefMapCrateData::new(krate.edition)), module_data, None, ); let (def_map, local_def_map) = collector::collect_defs(db, def_map, TreeId::new(root_file_id.into(), None), None); DefMapPair::new(db, def_map, local_def_map) } #[salsa_macros::tracked(returns(ref))] pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { let BlockLoc { ast_id, module } = block_id.lookup(db); let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit); let module_data = ModuleData::new(ModuleOrigin::BlockExpr { block: ast_id, id: block_id }, visibility, None); let krate = module.krate(db); let local_def_map = crate_local_def_map(db, krate); let def_map = DefMap::empty( db, krate, local_def_map.def_map(db).data.clone(), module_data, Some(BlockInfo { block: block_id, parent: module }), ); let (def_map, _) = collector::collect_defs( db, def_map, TreeId::new(ast_id.file_id, Some(block_id)), Some(local_def_map.local(db)), ); def_map } impl DefMap { pub fn edition(&self) -> Edition { self.data.edition } fn empty( db: &dyn DefDatabase, krate: Crate, crate_data: Arc, module_data: ModuleData, block: Option, ) -> DefMap { let mut modules = ModulesMap::new(); let root = unsafe { ModuleIdLt::new(db, krate, block.map(|it| it.block)).to_static() }; modules.insert(root, module_data); DefMap { block, root, modules, krate, prelude: None, macro_use_prelude: FxHashMap::default(), derive_helpers_in_scope: FxHashMap::default(), diagnostics: Vec::new(), data: crate_data, macro_def_to_macro_id: FxHashMap::default(), } } fn shrink_to_fit(&mut self) { // Exhaustive match to require handling new fields. let Self { macro_use_prelude, diagnostics, modules, derive_helpers_in_scope, root: _, block: _, krate: _, prelude: _, data: _, macro_def_to_macro_id, } = self; macro_def_to_macro_id.shrink_to_fit(); macro_use_prelude.shrink_to_fit(); diagnostics.shrink_to_fit(); modules.shrink_to_fit(); derive_helpers_in_scope.shrink_to_fit(); for (_, module) in modules.iter_mut() { module.children.shrink_to_fit(); module.scope.shrink_to_fit(); } } } impl DefMap { /// Returns all modules in the crate that are associated with the given file. pub fn modules_for_file<'a>( &'a self, db: &'a dyn DefDatabase, file_id: FileId, ) -> impl Iterator + 'a { self.modules .iter() .filter(move |(_, data)| { data.origin.file_id().map(|file_id| file_id.file_id(db)) == Some(file_id) }) .map(|(id, _)| id) } pub fn modules(&self) -> impl Iterator + '_ { self.modules.iter() } /// Returns all inline modules (mod name { ... }) in the crate that are associated with the given macro expansion. pub fn inline_modules_for_macro_file( &self, file_id: MacroCallId, ) -> impl Iterator + '_ { self.modules .iter() .filter(move |(_, data)| { matches!( data.origin, ModuleOrigin::Inline { definition_tree_id, .. } if definition_tree_id.file_id().macro_file() == Some(file_id) ) }) .map(|(id, _)| id) } pub fn derive_helpers_in_scope( &self, id: AstId, ) -> Option<&[(Name, MacroId, Either)]> { self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) } pub fn registered_tools(&self) -> &[Symbol] { &self.data.registered_tools } pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool { self.data.unstable_features.contains(feature) } pub fn is_rustc_coherence_is_core(&self) -> bool { self.data.rustc_coherence_is_core } pub fn is_no_std(&self) -> bool { self.data.no_std || self.data.no_core } pub fn is_no_core(&self) -> bool { self.data.no_core } pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { self.data.fn_proc_macro_mapping.get(&id).copied() } pub fn proc_macro_as_fn(&self, id: ProcMacroId) -> Option { self.data.fn_proc_macro_mapping_back.get(&id).copied() } pub fn krate(&self) -> Crate { self.krate } #[inline] pub fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { match self.block { Some(_) => crate_def_map(db, self.krate()).root, None => self.root, } } /// This is the same as [`Self::crate_root`] for crate def maps, but for block def maps, it /// returns the root block module. pub fn root_module_id(&self) -> ModuleId { self.root } /// If this `DefMap` is for a block expression, returns the module containing the block (which /// might again be a block, or a module inside a block). pub fn parent(&self) -> Option { Some(self.block?.parent) } /// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. pub fn containing_module(&self, local_mod: ModuleId) -> Option { match self[local_mod].parent { Some(parent) => Some(parent), None => self.block.map(|BlockInfo { parent, .. }| parent), } } /// Get a reference to the def map's diagnostics. pub fn diagnostics(&self) -> &[DefDiagnostic] { self.diagnostics.as_slice() } pub fn recursion_limit(&self) -> u32 { // 128 is the default in rustc self.data.recursion_limit.unwrap_or(128) } // FIXME: this can use some more human-readable format (ideally, an IR // even), as this should be a great debugging aid. pub fn dump(&self, db: &dyn DefDatabase) -> String { let mut buf = String::new(); let mut current_map = self; while let Some(block) = current_map.block { go(&mut buf, db, current_map, "(block scope)", current_map.root); buf.push('\n'); current_map = block.parent.def_map(db); } go(&mut buf, db, current_map, "crate", current_map.root); return buf; fn go(buf: &mut String, db: &dyn DefDatabase, map: &DefMap, path: &str, module: ModuleId) { format_to!(buf, "{}\n", path); map[module].scope.dump(db, buf); for (name, child) in map[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) { let path = format!("{path}::{}", name.display(db, Edition::LATEST)); buf.push('\n'); go(buf, db, map, &path, *child); } } } } impl DefMap { pub(crate) fn block_id(&self) -> Option { self.block.map(|block| block.block) } pub(crate) fn prelude(&self) -> Option<(ModuleId, Option)> { self.prelude } pub(crate) fn macro_use_prelude(&self) -> &FxHashMap)> { &self.macro_use_prelude } pub(crate) fn resolve_path( &self, local_def_map: &LocalDefMap, db: &dyn DefDatabase, original_module: ModuleId, path: &ModPath, shadow: BuiltinShadowMode, expected_macro_subns: Option, ) -> (PerNs, Option) { let res = self.resolve_path_fp_with_macro( local_def_map, db, ResolveMode::Other, original_module, path, shadow, expected_macro_subns, ); (res.resolved_def, res.segment_index) } /// The first `Option` points at the `Enum` segment in case of `Enum::Variant`, the second /// points at the unresolved segments. pub(crate) fn resolve_path_locally( &self, local_def_map: &LocalDefMap, db: &dyn DefDatabase, original_module: ModuleId, path: &ModPath, shadow: BuiltinShadowMode, ) -> (PerNs, Option, ResolvePathResultPrefixInfo) { let res = self.resolve_path_fp_with_macro_single( local_def_map, db, ResolveMode::Other, original_module, path, shadow, None, // Currently this function isn't used for macro resolution. ); (res.resolved_def, res.segment_index, res.prefix_info) } /// Ascends the `DefMap` hierarchy and calls `f` with every `DefMap` and containing module. /// /// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns /// `None`, iteration continues. pub(crate) fn with_ancestor_maps( &self, db: &dyn DefDatabase, local_mod: ModuleId, f: &mut dyn FnMut(&DefMap, ModuleId) -> Option, ) -> Option { if let Some(it) = f(self, local_mod) { return Some(it); } let mut block = self.block; while let Some(block_info) = block { let parent = block_info.parent.def_map(db); if let Some(it) = f(parent, block_info.parent) { return Some(it); } block = parent.block; } None } } impl ModuleData { pub(crate) fn new( origin: ModuleOrigin, visibility: Visibility, parent: Option, ) -> Self { ModuleData { origin, visibility, parent, children: Default::default(), scope: ItemScope::default(), } } /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile { self.origin.definition_source(db) } /// Same as [`ModuleData::definition_source`] but only returns the file id to prevent parsing the ASt. pub fn definition_source_file_id(&self) -> HirFileId { match self.origin { ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { definition.into() } ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id.file_id(), ModuleOrigin::BlockExpr { block, .. } => block.file_id, } } pub fn definition_source_range(&self, db: &dyn DefDatabase) -> InFile { match &self.origin { &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { InFile::new( definition.into(), ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID).to_range(db), ) } &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( definition_tree_id.file_id(), AstId::new(definition_tree_id.file_id(), definition).to_range(db), ), ModuleOrigin::BlockExpr { block, .. } => InFile::new(block.file_id, block.to_range(db)), } } /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. /// `None` for the crate root or block. pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option> { let decl = self.origin.declaration()?; let value = decl.to_node(db); Some(InFile { file_id: decl.file_id, value }) } /// Returns the range which declares this module, either a `mod foo;` or a `mod foo {}`. /// `None` for the crate root or block. pub fn declaration_source_range(&self, db: &dyn DefDatabase) -> Option> { let decl = self.origin.declaration()?; Some(InFile { file_id: decl.file_id, value: decl.to_range(db) }) } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ModuleSource { SourceFile(ast::SourceFile), Module(ast::Module), BlockExpr(ast::BlockExpr), } impl ModuleSource { pub fn node(&self) -> SyntaxNode { match self { ModuleSource::SourceFile(it) => it.syntax().clone(), ModuleSource::Module(it) => it.syntax().clone(), ModuleSource::BlockExpr(it) => it.syntax().clone(), } } } /// See `sub_namespace_match()`. #[derive(Clone, Copy, PartialEq, Eq)] pub enum MacroSubNs { /// Function-like macros, suffixed with `!`. Bang, /// Macros inside attributes, i.e. attribute macros and derive macros. Attr, } pub(crate) fn macro_styles_from_id(db: &dyn DefDatabase, macro_id: MacroId) -> MacroCallStyles { let expander = match macro_id { MacroId::Macro2Id(it) => it.lookup(db).expander, MacroId::MacroRulesId(it) => it.lookup(db).expander, MacroId::ProcMacroId(it) => { return match it.lookup(db).kind { ProcMacroKind::CustomDerive => MacroCallStyles::DERIVE, ProcMacroKind::Bang => MacroCallStyles::FN_LIKE, ProcMacroKind::Attr => MacroCallStyles::ATTR, }; } }; match expander { MacroExpander::Declarative { styles } => styles, // Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently. MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroCallStyles::FN_LIKE, MacroExpander::BuiltInAttr(_) => MacroCallStyles::ATTR, MacroExpander::BuiltInDerive(_) => MacroCallStyles::DERIVE, } } /// Quoted from [rustc]: /// Macro namespace is separated into two sub-namespaces, one for bang macros and /// one for attribute-like macros (attributes, derives). /// We ignore resolutions from one sub-namespace when searching names in scope for another. /// /// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75 fn sub_namespace_match( db: &dyn DefDatabase, macro_id: MacroId, expected: Option, ) -> bool { let candidate = macro_styles_from_id(db, macro_id); match expected { Some(MacroSubNs::Bang) => candidate.contains(MacroCallStyles::FN_LIKE), Some(MacroSubNs::Attr) => { candidate.contains(MacroCallStyles::ATTR) || candidate.contains(MacroCallStyles::DERIVE) } // If we aren't expecting a specific sub-namespace // (e.g. in `use` declarations), match any macro. None => true, } } /// A newtype wrapper around `FxHashMap` that implements `IndexMut`. #[derive(Debug, PartialEq, Eq)] pub struct ModulesMap { inner: FxIndexMap, } impl ModulesMap { fn new() -> Self { Self { inner: FxIndexMap::default() } } fn iter(&self) -> impl Iterator + '_ { self.inner.iter().map(|(&k, v)| (k, v)) } fn iter_mut(&mut self) -> impl Iterator + '_ { self.inner.iter_mut().map(|(&k, v)| (k, v)) } } impl Deref for ModulesMap { type Target = FxIndexMap; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for ModulesMap { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl Index for ModulesMap { type Output = ModuleData; fn index(&self, id: ModuleId) -> &ModuleData { self.inner.get(&id).unwrap_or_else(|| panic!("ModuleId not found in ModulesMap: {id:#?}")) } } impl IndexMut for ModulesMap { fn index_mut(&mut self, id: ModuleId) -> &mut ModuleData { self.inner .get_mut(&id) .unwrap_or_else(|| panic!("ModuleId not found in ModulesMap: {id:#?}")) } }