]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_def/src/nameres/collector.rs
internal: Record unresolved derive invocations in hir
[rust.git] / crates / hir_def / src / nameres / collector.rs
index b486beea7dc444dba2e959399d447cd8cbc48ef9..ca8afe8cbfd762633b0bba56cb083fa8add5cd85 100644 (file)
@@ -8,7 +8,6 @@
 use base_db::{CrateId, Edition, FileId, ProcMacroId};
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{
-    ast_id_map::FileAstId,
     builtin_attr_macro::find_builtin_attr,
     builtin_derive_macro::find_builtin_derive,
     builtin_fn_macro::find_builtin_macro,
@@ -21,7 +20,7 @@
 use la_arena::Idx;
 use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
-use syntax::ast;
+use syntax::{ast, SmolStr};
 
 use crate::{
     attr::{Attr, AttrId, AttrInput, Attrs},
     path::{ImportAlias, ModPath, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
-    AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
-    LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
-    UnresolvedMacro,
+    AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionLoc,
+    ImplLoc, Intern, ItemContainerId, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc,
+    TypeAliasLoc, UnionLoc, UnresolvedMacro,
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
 static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
 static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
 
-pub(super) fn collect_defs(
-    db: &dyn DefDatabase,
-    mut def_map: DefMap,
-    block: Option<AstId<ast::BlockExpr>>,
-) -> DefMap {
+pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap {
     let crate_graph = db.crate_graph();
 
     let mut deps = FxHashMap::default();
@@ -70,7 +65,7 @@ pub(super) fn collect_defs(
 
         deps.insert(dep.as_name(), dep_root.into());
 
-        if dep.is_prelude() && block.is_none() {
+        if dep.is_prelude() && !tree_id.is_block() {
             def_map.extern_prelude.insert(dep.as_name(), dep_root.into());
         }
     }
@@ -94,7 +89,6 @@ pub(super) fn collect_defs(
         glob_imports: FxHashMap::default(),
         unresolved_imports: Vec::new(),
         resolved_imports: Vec::new(),
-
         unresolved_macros: Vec::new(),
         mod_dirs: FxHashMap::default(),
         cfg_options,
@@ -106,13 +100,10 @@ pub(super) fn collect_defs(
         registered_attrs: Default::default(),
         registered_tools: Default::default(),
     };
-    match block {
-        Some(block) => {
-            collector.seed_with_inner(block);
-        }
-        None => {
-            collector.seed_with_top_level();
-        }
+    if tree_id.is_block() {
+        collector.seed_with_inner(tree_id);
+    } else {
+        collector.seed_with_top_level();
     }
     collector.collect();
     let mut def_map = collector.finish();
@@ -126,16 +117,15 @@ enum PartialResolvedImport {
     Unresolved,
     /// One of namespaces is resolved
     Indeterminate(PerNs),
-    /// All namespaces are resolved, OR it is came from other crate
+    /// All namespaces are resolved, OR it comes from other crate
     Resolved(PerNs),
 }
 
 impl PartialResolvedImport {
-    fn namespaces(&self) -> PerNs {
+    fn namespaces(self) -> PerNs {
         match self {
             PartialResolvedImport::Unresolved => PerNs::none(),
-            PartialResolvedImport::Indeterminate(ns) => *ns,
-            PartialResolvedImport::Resolved(ns) => *ns,
+            PartialResolvedImport::Indeterminate(ns) | PartialResolvedImport::Resolved(ns) => ns,
         }
     }
 }
@@ -223,20 +213,14 @@ struct MacroDirective {
     module_id: LocalModuleId,
     depth: usize,
     kind: MacroDirectiveKind,
+    container: ItemContainerId,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 enum MacroDirectiveKind {
     FnLike { ast_id: AstIdWithPath<ast::MacroCall>, expand_to: ExpandTo },
-    Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
-    Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem },
-}
-
-struct DefData<'a> {
-    id: ModuleDefId,
-    name: &'a Name,
-    visibility: &'a RawVisibility,
-    has_constructor: bool,
+    Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId, derive_pos: usize },
+    Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem, tree: TreeId },
 }
 
 /// Walks the tree of module recursively
@@ -268,9 +252,9 @@ struct DefCollector<'a> {
     /// attributes.
     derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
     /// Custom attributes registered with `#![register_attr]`.
-    registered_attrs: Vec<String>,
+    registered_attrs: Vec<SmolStr>,
     /// Custom tool modules registered with `#![register_tool]`.
-    registered_tools: Vec<String>,
+    registered_tools: Vec<SmolStr>,
 }
 
 impl DefCollector<'_> {
@@ -292,59 +276,59 @@ fn seed_with_top_level(&mut self) {
                     None => continue,
                 };
 
-                let registered_name = if *attr_name == hir_expand::name![register_attr]
-                    || *attr_name == hir_expand::name![register_tool]
-                {
-                    match attr.input.as_deref() {
-                        Some(AttrInput::TokenTree(subtree, _)) => match &*subtree.token_trees {
-                            [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(),
-                            _ => continue,
-                        },
-                        _ => continue,
-                    }
-                } else {
+                let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
+                    || *attr_name == hir_expand::name![register_tool];
+                if !attr_is_register_like {
                     continue;
+                }
+
+                let registered_name = match attr.input.as_deref() {
+                    Some(AttrInput::TokenTree(subtree, _)) => match &*subtree.token_trees {
+                        [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(),
+                        _ => continue,
+                    },
+                    _ => continue,
                 };
 
                 if *attr_name == hir_expand::name![register_attr] {
-                    self.registered_attrs.push(registered_name.to_string());
+                    self.registered_attrs.push(registered_name.to_smol_str());
                     cov_mark::hit!(register_attr);
                 } else {
-                    self.registered_tools.push(registered_name.to_string());
+                    self.registered_tools.push(registered_name.to_smol_str());
                     cov_mark::hit!(register_tool);
                 }
             }
 
             ModCollector {
-                def_collector: &mut *self,
+                def_collector: self,
                 macro_depth: 0,
                 module_id,
                 tree_id: TreeId::new(file_id.into(), None),
                 item_tree: &item_tree,
                 mod_dir: ModDir::root(),
             }
-            .collect(item_tree.top_level_items());
+            .collect_in_top_module(item_tree.top_level_items());
         }
     }
 
-    fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) {
-        let item_tree = self.db.file_item_tree(block.file_id);
+    fn seed_with_inner(&mut self, tree_id: TreeId) {
+        let item_tree = tree_id.item_tree(self.db);
         let module_id = self.def_map.root;
-        if item_tree
+
+        let is_cfg_enabled = item_tree
             .top_level_attrs(self.db, self.def_map.krate)
             .cfg()
-            .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false))
-        {
+            .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false));
+        if is_cfg_enabled {
             ModCollector {
-                def_collector: &mut *self,
+                def_collector: self,
                 macro_depth: 0,
                 module_id,
-                // FIXME: populate block once we have per-block ItemTrees
-                tree_id: TreeId::new(block.file_id, None),
+                tree_id,
                 item_tree: &item_tree,
                 mod_dir: ModDir::root(),
             }
-            .collect(item_tree.inner_items_of_block(block.value));
+            .collect_in_top_module(item_tree.top_level_items());
         }
     }
 
@@ -437,21 +421,20 @@ fn reseed_with_unresolved_attribute(&mut self) -> ReachedFixedPoint {
 
         let mut unresolved_macros = std::mem::take(&mut self.unresolved_macros);
         let pos = unresolved_macros.iter().position(|directive| {
-            if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
+            if let MacroDirectiveKind::Attr { ast_id, mod_item, attr, tree } = &directive.kind {
                 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id);
 
-                let file_id = ast_id.ast_id.file_id;
-                let item_tree = self.db.file_item_tree(file_id);
+                let item_tree = tree.item_tree(self.db);
                 let mod_dir = self.mod_dirs[&directive.module_id].clone();
                 ModCollector {
-                    def_collector: &mut *self,
+                    def_collector: self,
                     macro_depth: directive.depth,
                     module_id: directive.module_id,
-                    tree_id: TreeId::new(file_id, None),
+                    tree_id: *tree,
                     item_tree: &item_tree,
                     mod_dir,
                 }
-                .collect(&[*mod_item]);
+                .collect(&[*mod_item], directive.container);
                 true
             } else {
                 false
@@ -522,16 +505,16 @@ fn inject_prelude(&mut self, crate_attrs: &Attrs) {
                 BuiltinShadowMode::Other,
             );
 
-            match &per_ns.types {
+            match per_ns.types {
                 Some((ModuleDefId::ModuleId(m), _)) => {
-                    self.def_map.prelude = Some(*m);
+                    self.def_map.prelude = Some(m);
                     return;
                 }
-                _ => {
+                types => {
                     tracing::debug!(
                         "could not resolve prelude path `{}` to module (resolved to {:?})",
                         path,
-                        per_ns.types
+                        types
                     );
                 }
             }
@@ -558,9 +541,9 @@ fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) {
         let kind = def.kind.to_basedb_kind();
         self.exports_proc_macros = true;
         let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
-            Some((_, expander)) => MacroDefId {
+            Some(&(_, expander)) => MacroDefId {
                 krate: self.def_map.krate,
-                kind: MacroDefKind::ProcMacro(*expander, kind, ast_id),
+                kind: MacroDefKind::ProcMacro(expander, kind, ast_id),
                 local_inner: false,
             },
             None => MacroDefId {
@@ -612,6 +595,7 @@ fn define_macro_rules(
     ) {
         // Textual scoping
         self.define_legacy_macro(module_id, name.clone(), macro_);
+        self.def_map.modules[module_id].scope.declare_macro(macro_);
 
         // Module scoping
         // In Rust, `#[macro_export]` macros are unconditionally visible at the
@@ -650,6 +634,7 @@ fn define_macro_def(
     ) {
         let vis =
             self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public);
+        self.def_map.modules[module_id].scope.declare_macro(macro_);
         self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named);
     }
 
@@ -658,6 +643,7 @@ fn define_macro_def(
     /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
     /// And unconditionally exported.
     fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
+        self.def_map.modules[self.def_map.root].scope.declare_macro(macro_);
         self.update(
             self.def_map.root,
             &[(Some(name), PerNs::macros(macro_, Visibility::Public))],
@@ -786,18 +772,16 @@ fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialRe
     }
 
     fn resolve_extern_crate(&self, name: &Name) -> PerNs {
-        let arc;
-        let root = match self.def_map.block {
-            Some(_) => {
-                arc = self.def_map.crate_root(self.db).def_map(self.db);
-                &*arc
-            }
-            None => &self.def_map,
-        };
-
-        if name == &name!(self) {
+        if *name == name!(self) {
             cov_mark::hit!(extern_crate_self_as);
-            PerNs::types(root.module_id(root.root()).into(), Visibility::Public)
+            let root = match self.def_map.block {
+                Some(_) => {
+                    let def_map = self.def_map.crate_root(self.db).def_map(self.db);
+                    def_map.module_id(def_map.root())
+                }
+                None => self.def_map.module_id(self.def_map.root()),
+            };
+            PerNs::types(root.into(), Visibility::Public)
         } else {
             self.deps.get(name).map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
         }
@@ -817,10 +801,10 @@ fn record_resolved_import(&mut self, directive: &ImportDirective) {
         match import.kind {
             ImportKind::Plain | ImportKind::TypeOnly => {
                 let name = match &import.alias {
-                    Some(ImportAlias::Alias(name)) => Some(name.clone()),
+                    Some(ImportAlias::Alias(name)) => Some(name),
                     Some(ImportAlias::Underscore) => None,
                     None => match import.path.segments().last() {
-                        Some(last_segment) => Some(last_segment.clone()),
+                        Some(last_segment) => Some(last_segment),
                         None => {
                             cov_mark::hit!(bogus_paths);
                             return;
@@ -837,12 +821,12 @@ fn record_resolved_import(&mut self, directive: &ImportDirective) {
 
                 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
                 if import.is_extern_crate && module_id == self.def_map.root {
-                    if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
+                    if let (Some(def), Some(name)) = (def.take_types(), name) {
                         self.def_map.extern_prelude.insert(name.clone(), def);
                     }
                 }
 
-                self.update(module_id, &[(name, def)], vis, ImportType::Named);
+                self.update(module_id, &[(name.cloned(), def)], vis, ImportType::Named);
             }
             ImportKind::Glob => {
                 tracing::debug!("glob import: {:?}", import);
@@ -1061,88 +1045,162 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
 
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
-                    match macro_call_as_call_id(
+                    let call_id = macro_call_as_call_id(
                         ast_id,
                         *expand_to,
                         self.db,
                         self.def_map.krate,
                         &resolver,
                         &mut |_err| (),
-                    ) {
-                        Ok(Ok(call_id)) => {
-                            resolved.push((directive.module_id, call_id, directive.depth));
-                            res = ReachedFixedPoint::No;
-                            return false;
-                        }
-                        Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {}
+                    );
+                    if let Ok(Ok(call_id)) = call_id {
+                        resolved.push((
+                            directive.module_id,
+                            call_id,
+                            directive.depth,
+                            directive.container,
+                        ));
+                        res = ReachedFixedPoint::No;
+                        return false;
                     }
                 }
-                MacroDirectiveKind::Derive { ast_id, derive_attr } => {
-                    match derive_macro_as_call_id(
+                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => {
+                    let call_id = derive_macro_as_call_id(
                         ast_id,
                         *derive_attr,
                         self.db,
                         self.def_map.krate,
                         &resolver,
-                    ) {
-                        Ok(call_id) => {
-                            self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
-                                ast_id.ast_id,
-                                call_id,
-                                *derive_attr,
-                            );
+                    );
+                    if let Ok(call_id) = call_id {
+                        self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc(
+                            ast_id.ast_id,
+                            call_id,
+                            *derive_attr,
+                            *derive_pos,
+                        );
 
-                            resolved.push((directive.module_id, call_id, directive.depth));
-                            res = ReachedFixedPoint::No;
-                            return false;
-                        }
-                        Err(UnresolvedMacro { .. }) => (),
+                        resolved.push((
+                            directive.module_id,
+                            call_id,
+                            directive.depth,
+                            directive.container,
+                        ));
+                        res = ReachedFixedPoint::No;
+                        return false;
                     }
                 }
-                MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
-                    if let Some(ident) = ast_id.path.as_ident() {
-                        if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
+                MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr, tree } => {
+                    let &AstIdWithPath { ast_id, ref path } = file_ast_id;
+                    let file_id = ast_id.file_id;
+
+                    let mut recollect_without = |collector: &mut Self| {
+                        // Remove the original directive since we resolved it.
+                        let mod_dir = collector.mod_dirs[&directive.module_id].clone();
+                        collector.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
+
+                        let item_tree = tree.item_tree(self.db);
+                        ModCollector {
+                            def_collector: collector,
+                            macro_depth: directive.depth,
+                            module_id: directive.module_id,
+                            tree_id: *tree,
+                            item_tree: &item_tree,
+                            mod_dir,
+                        }
+                        .collect(&[*mod_item], directive.container);
+                        res = ReachedFixedPoint::No;
+                        false
+                    };
+
+                    if let Some(ident) = path.as_ident() {
+                        if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) {
                             if helpers.contains(ident) {
                                 cov_mark::hit!(resolved_derive_helper);
-
                                 // Resolved to derive helper. Collect the item's attributes again,
                                 // starting after the derive helper.
-                                let file_id = ast_id.ast_id.file_id;
-                                let item_tree = self.db.file_item_tree(file_id);
-                                let mod_dir = self.mod_dirs[&directive.module_id].clone();
-                                self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
-                                ModCollector {
-                                    def_collector: &mut *self,
-                                    macro_depth: directive.depth,
-                                    module_id: directive.module_id,
-                                    tree_id: TreeId::new(file_id, None),
-                                    item_tree: &item_tree,
-                                    mod_dir,
+                                return recollect_without(self);
+                            }
+                        }
+                    }
+
+                    let def = resolver(path.clone()).filter(MacroDefId::is_attribute);
+                    if matches!(
+                        def,
+                        Some(MacroDefId {  kind:MacroDefKind::BuiltInAttr(expander, _),.. })
+                        if expander.is_derive()
+                    ) {
+                        // Resolved to `#[derive]`
+
+                        match mod_item {
+                            ModItem::Struct(_) | ModItem::Union(_) | ModItem::Enum(_) => (),
+                            _ => {
+                                let diag = DefDiagnostic::invalid_derive_target(
+                                    directive.module_id,
+                                    ast_id,
+                                    attr.id,
+                                );
+                                self.def_map.diagnostics.push(diag);
+                                return recollect_without(self);
+                            }
+                        }
+
+                        match attr.parse_derive() {
+                            Some(derive_macros) => {
+                                let mut len = 0;
+                                for (idx, path) in derive_macros.enumerate() {
+                                    let ast_id = AstIdWithPath::new(file_id, ast_id.value, path);
+                                    self.unresolved_macros.push(MacroDirective {
+                                        module_id: directive.module_id,
+                                        depth: directive.depth + 1,
+                                        kind: MacroDirectiveKind::Derive {
+                                            ast_id,
+                                            derive_attr: attr.id,
+                                            derive_pos: idx,
+                                        },
+                                        container: directive.container,
+                                    });
+                                    len = idx;
                                 }
-                                .collect(&[*mod_item]);
 
-                                // Remove the original directive since we resolved it.
-                                res = ReachedFixedPoint::No;
-                                return false;
+                                self.def_map.modules[directive.module_id]
+                                    .scope
+                                    .init_derive_attribute(ast_id, attr.id, len + 1);
+                            }
+                            None => {
+                                let diag = DefDiagnostic::malformed_derive(
+                                    directive.module_id,
+                                    ast_id,
+                                    attr.id,
+                                );
+                                self.def_map.diagnostics.push(diag);
                             }
                         }
+
+                        return recollect_without(self);
                     }
 
                     if !self.db.enable_proc_attr_macros() {
                         return true;
                     }
 
-                    // Not resolved to a derive helper, so try to resolve as a macro.
-                    match attr_macro_as_call_id(
-                        ast_id,
-                        attr,
-                        self.db,
-                        self.def_map.krate,
-                        &resolver,
-                    ) {
+                    // Not resolved to a derive helper or the derive attribute, so try to resolve as a normal attribute.
+                    match attr_macro_as_call_id(file_ast_id, attr, self.db, self.def_map.krate, def)
+                    {
                         Ok(call_id) => {
                             let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
-                            if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind {
+
+                            // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
+                            // due to duplicating functions into macro expansions
+                            if matches!(
+                                loc.def.kind,
+                                MacroDefKind::BuiltInAttr(expander, _)
+                                if expander.is_test() || expander.is_bench()
+                            ) {
+                                return recollect_without(self);
+                            }
+
+                            if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
                                 if exp.is_dummy() {
                                     // Proc macros that cannot be expanded are treated as not
                                     // resolved, in order to fall back later.
@@ -1153,31 +1211,20 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
                                         ),
                                     );
 
-                                    let file_id = ast_id.ast_id.file_id;
-                                    let item_tree = self.db.file_item_tree(file_id);
-                                    let mod_dir = self.mod_dirs[&directive.module_id].clone();
-                                    self.skip_attrs
-                                        .insert(InFile::new(file_id, *mod_item), attr.id);
-                                    ModCollector {
-                                        def_collector: &mut *self,
-                                        macro_depth: directive.depth,
-                                        module_id: directive.module_id,
-                                        tree_id: TreeId::new(file_id, None),
-                                        item_tree: &item_tree,
-                                        mod_dir,
-                                    }
-                                    .collect(&[*mod_item]);
-
-                                    // Remove the macro directive.
-                                    return false;
+                                    return recollect_without(self);
                                 }
                             }
 
                             self.def_map.modules[directive.module_id]
                                 .scope
-                                .add_attr_macro_invoc(ast_id.ast_id, call_id);
+                                .add_attr_macro_invoc(ast_id, call_id);
 
-                            resolved.push((directive.module_id, call_id, directive.depth));
+                            resolved.push((
+                                directive.module_id,
+                                call_id,
+                                directive.depth,
+                                directive.container,
+                            ));
                             res = ReachedFixedPoint::No;
                             return false;
                         }
@@ -1191,8 +1238,8 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
         // Attribute resolution can add unresolved macro invocations, so concatenate the lists.
         self.unresolved_macros.extend(macros);
 
-        for (module_id, macro_call_id, depth) in resolved {
-            self.collect_macro_expansion(module_id, macro_call_id, depth);
+        for (module_id, macro_call_id, depth, container) in resolved {
+            self.collect_macro_expansion(module_id, macro_call_id, depth, container);
         }
 
         res
@@ -1203,6 +1250,7 @@ fn collect_macro_expansion(
         module_id: LocalModuleId,
         macro_call_id: MacroCallId,
         depth: usize,
+        container: ItemContainerId,
     ) {
         if EXPANSION_DEPTH_LIMIT.check(depth).is_err() {
             cov_mark::hit!(macro_expansion_overflow);
@@ -1254,7 +1302,7 @@ fn collect_macro_expansion(
             item_tree: &item_tree,
             mod_dir,
         }
-        .collect(item_tree.top_level_items());
+        .collect(item_tree.top_level_items(), container);
     }
 
     fn finish(mut self) -> DefMap {
@@ -1265,7 +1313,7 @@ fn finish(mut self) -> DefMap {
         for directive in &self.unresolved_macros {
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
-                    match macro_call_as_call_id(
+                    let macro_call_as_call_id = macro_call_as_call_id(
                         ast_id,
                         *expand_to,
                         self.db,
@@ -1281,15 +1329,13 @@ fn finish(mut self) -> DefMap {
                             resolved_res.resolved_def.take_macros()
                         },
                         &mut |_| (),
-                    ) {
-                        Ok(_) => (),
-                        Err(UnresolvedMacro { path }) => {
-                            self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
-                                directive.module_id,
-                                ast_id.ast_id,
-                                path,
-                            ));
-                        }
+                    );
+                    if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
+                        self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+                            directive.module_id,
+                            ast_id.ast_id,
+                            path,
+                        ));
                     }
                 }
                 MacroDirectiveKind::Derive { .. } | MacroDirectiveKind::Attr { .. } => {
@@ -1321,19 +1367,18 @@ fn finish(mut self) -> DefMap {
         }
 
         for directive in &self.unresolved_imports {
-            if let ImportSource::Import { id: import, use_tree } = &directive.import.source {
-                if let (Some(krate), PathKind::Plain | PathKind::Abs) =
-                    (directive.import.path.segments().first(), &directive.import.path.kind)
-                {
-                    if diagnosed_extern_crates.contains(krate) {
-                        continue;
-                    }
+            if let ImportSource::Import { id: import, use_tree } = directive.import.source {
+                if matches!(
+                    (directive.import.path.segments().first(), &directive.import.path.kind),
+                    (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
+                ) {
+                    continue;
                 }
 
                 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
                     directive.module_id,
-                    *import,
-                    *use_tree,
+                    import,
+                    use_tree,
                 ));
             }
         }
@@ -1353,7 +1398,19 @@ struct ModCollector<'a, 'b> {
 }
 
 impl ModCollector<'_, '_> {
-    fn collect(&mut self, items: &[ModItem]) {
+    fn collect_in_top_module(&mut self, items: &[ModItem]) {
+        let module = self.def_collector.def_map.module_id(self.module_id);
+        self.collect(items, module.into())
+    }
+
+    fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
+        struct DefData<'a> {
+            id: ModuleDefId,
+            name: &'a Name,
+            visibility: &'a RawVisibility,
+            has_constructor: bool,
+        }
+
         let krate = self.def_collector.def_map.krate;
 
         // Note: don't assert that inserted value is fresh: it's simply not true
@@ -1371,18 +1428,18 @@ fn collect(&mut self, items: &[ModItem]) {
         // This should be processed eagerly instead of deferred to resolving.
         // `#[macro_use] extern crate` is hoisted to imports macros before collecting
         // any other items.
-        for item in items {
-            let attrs = self.item_tree.attrs(self.def_collector.db, krate, (*item).into());
+        for &item in items {
+            let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
             if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
                 if let ModItem::ExternCrate(id) = item {
-                    let import = self.item_tree[*id].clone();
+                    let import = &self.item_tree[id];
                     let attrs = self.item_tree.attrs(
                         self.def_collector.db,
                         krate,
-                        ModItem::from(*id).into(),
+                        ModItem::from(id).into(),
                     );
                     if attrs.by_key("macro_use").exists() {
-                        self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
+                        self.def_collector.import_macros_from_extern_crate(self.module_id, import);
                     }
                 }
             }
@@ -1397,7 +1454,7 @@ fn collect(&mut self, items: &[ModItem]) {
                 }
             }
 
-            if let Err(()) = self.resolve_attributes(&attrs, item) {
+            if let Err(()) = self.resolve_attributes(&attrs, item, container) {
                 // Do not process the item. It has at least one non-builtin attribute, so the
                 // fixed-point algorithm is required to resolve the rest of them.
                 continue;
@@ -1436,8 +1493,17 @@ fn collect(&mut self, items: &[ModItem]) {
                         status: PartialResolvedImport::Unresolved,
                     })
                 }
-                ModItem::ExternBlock(block) => self.collect(&self.item_tree[block].children),
-                ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]),
+                ModItem::ExternBlock(block) => self.collect(
+                    &self.item_tree[block].children,
+                    ItemContainerId::ExternBlockId(
+                        ExternBlockLoc {
+                            container: module,
+                            id: ItemTreeId::new(self.tree_id, block),
+                        }
+                        .intern(self.def_collector.db),
+                    ),
+                ),
+                ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac], container),
                 ModItem::MacroRules(id) => self.collect_macro_rules(id),
                 ModItem::MacroDef(id) => self.collect_macro_def(id),
                 ModItem::Impl(imp) => {
@@ -1454,12 +1520,9 @@ fn collect(&mut self, items: &[ModItem]) {
                     self.collect_proc_macro_def(&func.name, ast_id, &attrs);
 
                     def = Some(DefData {
-                        id: FunctionLoc {
-                            container: module.into(),
-                            id: ItemTreeId::new(self.tree_id, id),
-                        }
-                        .intern(self.def_collector.db)
-                        .into(),
+                        id: FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }
+                            .intern(self.def_collector.db)
+                            .into(),
                         name: &func.name,
                         visibility: &self.item_tree[func.visibility],
                         has_constructor: false,
@@ -1503,11 +1566,8 @@ fn collect(&mut self, items: &[ModItem]) {
                 }
                 ModItem::Const(id) => {
                     let it = &self.item_tree[id];
-                    let const_id = ConstLoc {
-                        container: module.into(),
-                        id: ItemTreeId::new(self.tree_id, id),
-                    }
-                    .intern(self.def_collector.db);
+                    let const_id = ConstLoc { container, id: ItemTreeId::new(self.tree_id, id) }
+                        .intern(self.def_collector.db);
 
                     match &it.name {
                         Some(name) => {
@@ -1530,7 +1590,7 @@ fn collect(&mut self, items: &[ModItem]) {
                     let it = &self.item_tree[id];
 
                     def = Some(DefData {
-                        id: StaticLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                        id: StaticLoc { container, id: ItemTreeId::new(self.tree_id, id) }
                             .intern(self.def_collector.db)
                             .into(),
                         name: &it.name,
@@ -1554,12 +1614,9 @@ fn collect(&mut self, items: &[ModItem]) {
                     let it = &self.item_tree[id];
 
                     def = Some(DefData {
-                        id: TypeAliasLoc {
-                            container: module.into(),
-                            id: ItemTreeId::new(self.tree_id, id),
-                        }
-                        .intern(self.def_collector.db)
-                        .into(),
+                        id: TypeAliasLoc { container, id: ItemTreeId::new(self.tree_id, id) }
+                            .intern(self.def_collector.db)
+                            .into(),
                         name: &it.name,
                         visibility: &self.item_tree[it.visibility],
                         has_constructor: false,
@@ -1607,7 +1664,7 @@ fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
                         item_tree: self.item_tree,
                         mod_dir,
                     }
-                    .collect(&*items);
+                    .collect_in_top_module(&*items);
                     if is_macro_use {
                         self.import_all_legacy_macros(module_id);
                     }
@@ -1624,9 +1681,7 @@ fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
                         let is_enabled = item_tree
                             .top_level_attrs(db, self.def_collector.def_map.krate)
                             .cfg()
-                            .map_or(true, |cfg| {
-                                self.def_collector.cfg_options.check(&cfg) != Some(false)
-                            });
+                            .map_or(true, |cfg| self.is_cfg_enabled(&cfg));
                         if is_enabled {
                             let module_id = self.push_child_module(
                                 module.name.clone(),
@@ -1642,13 +1697,13 @@ fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
                                 item_tree: &item_tree,
                                 mod_dir,
                             }
-                            .collect(item_tree.top_level_items());
-                            if is_macro_use
+                            .collect_in_top_module(item_tree.top_level_items());
+                            let is_macro_use = is_macro_use
                                 || item_tree
                                     .top_level_attrs(db, self.def_collector.def_map.krate)
                                     .by_key("macro_use")
-                                    .exists()
-                            {
+                                    .exists();
+                            if is_macro_use {
                                 self.import_all_legacy_macros(module_id);
                             }
                         }
@@ -1682,14 +1737,17 @@ fn push_child_module(
                 ModuleOrigin::File { declaration, definition, is_mod_rs }
             }
         };
+
         let res = modules.alloc(ModuleData::new(origin, vis));
         modules[res].parent = Some(self.module_id);
         for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
             modules[res].scope.define_legacy_macro(name, mac)
         }
         modules[self.module_id].children.insert(name.clone(), res);
+
         let module = self.def_collector.def_map.module_id(res);
-        let def: ModuleDefId = module.into();
+        let def = ModuleDefId::from(module);
+
         self.def_collector.def_map.modules[self.module_id].scope.declare(def);
         self.def_collector.update(
             self.module_id,
@@ -1707,7 +1765,12 @@ fn push_child_module(
     ///
     /// If `ignore_up_to` is `Some`, attributes preceding and including that attribute will be
     /// assumed to be resolved already.
-    fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
+    fn resolve_attributes(
+        &mut self,
+        attrs: &Attrs,
+        mod_item: ModItem,
+        container: ItemContainerId,
+    ) -> Result<(), ()> {
         let mut ignore_up_to =
             self.def_collector.skip_attrs.get(&InFile::new(self.file_id(), mod_item)).copied();
         let iter = attrs
@@ -1731,90 +1794,65 @@ fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(),
             });
 
         for attr in iter {
-            if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
-                self.collect_derive(attr, mod_item);
-            } else if self.is_builtin_or_registered_attr(&attr.path) {
+            if self.is_builtin_or_registered_attr(&attr.path) {
                 continue;
-            } else {
-                tracing::debug!("non-builtin attribute {}", attr.path);
+            }
+            tracing::debug!("non-builtin attribute {}", attr.path);
 
-                let ast_id = AstIdWithPath::new(
-                    self.file_id(),
-                    mod_item.ast_id(self.item_tree),
-                    attr.path.as_ref().clone(),
-                );
-                self.def_collector.unresolved_macros.push(MacroDirective {
-                    module_id: self.module_id,
-                    depth: self.macro_depth + 1,
-                    kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
-                });
+            let ast_id = AstIdWithPath::new(
+                self.file_id(),
+                mod_item.ast_id(self.item_tree),
+                attr.path.as_ref().clone(),
+            );
+            self.def_collector.unresolved_macros.push(MacroDirective {
+                module_id: self.module_id,
+                depth: self.macro_depth + 1,
+                kind: MacroDirectiveKind::Attr {
+                    ast_id,
+                    attr: attr.clone(),
+                    mod_item,
+                    tree: self.tree_id,
+                },
+                container,
+            });
 
-                return Err(());
-            }
+            return Err(());
         }
 
         Ok(())
     }
 
     fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
-        if path.kind == PathKind::Plain {
-            if let Some(tool_module) = path.segments().first() {
-                let tool_module = tool_module.to_smol_str();
-                let is_tool = builtin_attr::TOOL_MODULES
-                    .iter()
-                    .copied()
-                    .chain(self.def_collector.registered_tools.iter().map(AsRef::as_ref))
-                    .any(|m| tool_module == *m);
-                if is_tool {
-                    return true;
-                }
+        if path.kind != PathKind::Plain {
+            return false;
+        }
+
+        let segments = path.segments();
+
+        if let Some(name) = segments.first() {
+            let name = name.to_smol_str();
+            let pred = |n: &_| *n == name;
+
+            let registered = self.def_collector.registered_tools.iter().map(SmolStr::as_str);
+            let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+            // FIXME: tool modules can be shadowed by actual modules
+            if is_tool {
+                return true;
             }
 
-            if let Some(name) = path.as_ident() {
-                let name = name.to_smol_str();
+            if segments.len() == 1 {
+                let registered = self.def_collector.registered_attrs.iter().map(SmolStr::as_str);
                 let is_inert = builtin_attr::INERT_ATTRIBUTES
                     .iter()
-                    .chain(builtin_attr::EXTRA_ATTRIBUTES)
-                    .copied()
-                    .chain(self.def_collector.registered_attrs.iter().map(AsRef::as_ref))
-                    .any(|attr| name == *attr);
+                    .map(|it| it.name)
+                    .chain(registered)
+                    .any(pred);
                 return is_inert;
             }
         }
-
         false
     }
 
-    fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
-        let ast_id: FileAstId<ast::Item> = match mod_item {
-            ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
-            ModItem::Union(it) => self.item_tree[it].ast_id.upcast(),
-            ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(),
-            _ => {
-                // Cannot use derive on this item.
-                // FIXME: diagnose
-                return;
-            }
-        };
-
-        match attr.parse_derive() {
-            Some(derive_macros) => {
-                for path in derive_macros {
-                    let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path);
-                    self.def_collector.unresolved_macros.push(MacroDirective {
-                        module_id: self.module_id,
-                        depth: self.macro_depth + 1,
-                        kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id },
-                    });
-                }
-            }
-            None => {
-                // FIXME: diagnose
-                tracing::debug!("malformed derive: {:?}", attr);
-            }
-        }
-    }
-
     /// If `attrs` registers a procedural macro, collects its definition.
     fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
         // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
@@ -1833,7 +1871,7 @@ fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
 
         let is_export = export_attr.exists();
         let is_local_inner = if is_export {
-            export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
+            export_attr.tt_values().flat_map(|it| &it.token_trees).any(|it| match it {
                 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
                     ident.text.contains("local_inner_macros")
                 }
@@ -1854,12 +1892,14 @@ fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
                     &name
                 }
                 None => {
-                    match attrs.by_key("rustc_builtin_macro").tt_values().next().and_then(|tt| {
-                        match tt.token_trees.first() {
-                            Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
-                            _ => None,
-                        }
-                    }) {
+                    let explicit_name =
+                        attrs.by_key("rustc_builtin_macro").tt_values().next().and_then(|tt| {
+                            match tt.token_trees.first() {
+                                Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name),
+                                _ => None,
+                            }
+                        });
+                    match explicit_name {
                         Some(ident) => {
                             name = ident.as_name();
                             &name
@@ -1948,8 +1988,8 @@ fn collect_macro_def(&mut self, id: FileItemTreeId<MacroDef>) {
         );
     }
 
-    fn collect_macro_call(&mut self, mac: &MacroCall) {
-        let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, (*mac.path).clone());
+    fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
+        let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
 
         // Case 1: try to resolve in legacy scope and expand macro_rules
         let mut error = None;
@@ -1978,6 +2018,7 @@ fn collect_macro_call(&mut self, mac: &MacroCall) {
                     self.module_id,
                     macro_call_id,
                     self.macro_depth + 1,
+                    container,
                 );
 
                 if let Some(err) = error {
@@ -2008,6 +2049,7 @@ fn collect_macro_call(&mut self, mac: &MacroCall) {
             module_id: self.module_id,
             depth: self.macro_depth + 1,
             kind: MacroDirectiveKind::FnLike { ast_id, expand_to: mac.expand_to },
+            container,
         });
     }