]> 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 d2c0175982d546a1191e17173f4a6a97f1244f3b..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,
     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());
         }
     }
@@ -105,9 +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();
@@ -217,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
@@ -317,12 +307,12 @@ fn seed_with_top_level(&mut self) {
                 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;
 
         let is_cfg_enabled = item_tree
@@ -334,12 +324,11 @@ fn seed_with_inner(&mut self, block: AstId<ast::BlockExpr>) {
                 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());
         }
     }
 
@@ -432,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: 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
@@ -607,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
@@ -645,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);
     }
 
@@ -653,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))],
@@ -781,7 +772,7 @@ fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialRe
     }
 
     fn resolve_extern_crate(&self, name: &Name) -> PerNs {
-        if name == &name!(self) {
+        if *name == name!(self) {
             cov_mark::hit!(extern_crate_self_as);
             let root = match self.def_map.block {
                 Some(_) => {
@@ -1054,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.
@@ -1146,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;
                         }
@@ -1184,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
@@ -1196,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);
@@ -1247,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 {
@@ -1258,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,
@@ -1274,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 { .. } => {
@@ -1314,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,
                 ));
             }
         }
@@ -1346,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
@@ -1364,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);
                     }
                 }
             }
@@ -1390,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;
@@ -1429,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) => {
@@ -1447,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,
@@ -1496,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) => {
@@ -1523,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,
@@ -1547,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,
@@ -1600,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);
                     }
@@ -1617,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(),
@@ -1635,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);
                             }
                         }
@@ -1675,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,
@@ -1700,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
@@ -1724,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(SmolStr::as_str))
-                    .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(SmolStr::as_str))
-                    .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
@@ -1826,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")
                 }
@@ -1847,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
@@ -1941,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;
@@ -1971,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 {
@@ -2001,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,
         });
     }