about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/attr.rs3
-rw-r--r--crates/hir-def/src/child_by_source.rs74
-rw-r--r--crates/hir-def/src/data/adt.rs48
-rw-r--r--crates/hir-def/src/generics.rs174
-rw-r--r--crates/hir-def/src/item_tree.rs29
-rw-r--r--crates/hir-def/src/item_tree/lower.rs2
-rw-r--r--crates/hir-def/src/lib.rs405
-rw-r--r--crates/hir-def/src/resolver.rs41
-rw-r--r--crates/hir-def/src/src.rs109
-rw-r--r--crates/hir-def/src/trace.rs2
-rw-r--r--crates/hir-def/src/visibility.rs119
-rw-r--r--crates/ide-diagnostics/src/handlers/private_field.rs26
12 files changed, 552 insertions, 480 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 85c0542ad3d..c91a5497262 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -28,7 +28,8 @@ use crate::{
     lang_item::LangItem,
     nameres::{ModuleOrigin, ModuleSource},
     src::{HasChildSource, HasSource},
-    AdtId, AttrDefId, GenericParamId, ItemTreeLoc, LocalFieldId, Lookup, MacroId, VariantId,
+    AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
+    VariantId,
 };
 
 /// Desugared attributes of an item post `cfg_attr` expansion.
diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs
index 5efa3e8d9e0..ba7d06272af 100644
--- a/crates/hir-def/src/child_by_source.rs
+++ b/crates/hir-def/src/child_by_source.rs
@@ -6,6 +6,7 @@
 
 use either::Either;
 use hir_expand::{attrs::collect_attrs, HirFileId};
+use syntax::ast;
 
 use crate::{
     db::DefDatabase,
@@ -17,8 +18,9 @@ use crate::{
     item_tree::ItemTreeNode,
     nameres::DefMap,
     src::{HasChildSource, HasSource},
-    AdtId, AssocItemId, DefWithBodyId, EnumId, FieldId, ImplId, ItemTreeLoc, Lookup, MacroId,
-    ModuleDefId, ModuleId, TraitId, VariantId,
+    AdtId, AssocItemId, DefWithBodyId, EnumId, FieldId, GenericDefId, ImplId, ItemTreeLoc,
+    LifetimeParamId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, TypeOrConstParamId,
+    VariantId,
 };
 
 pub trait ChildBySource {
@@ -59,14 +61,6 @@ impl ChildBySource for ImplId {
     }
 }
 
-fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
-    match item {
-        AssocItemId::FunctionId(func) => insert_item_loc(db, res, file_id, func, keys::FUNCTION),
-        AssocItemId::ConstId(konst) => insert_item_loc(db, res, file_id, konst, keys::CONST),
-        AssocItemId::TypeAliasId(ty) => insert_item_loc(db, res, file_id, ty, keys::TYPE_ALIAS),
-    }
-}
-
 impl ChildBySource for ModuleId {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         let def_map = self.def_map(db);
@@ -118,14 +112,6 @@ impl ChildBySource for ItemScope {
             file_id: HirFileId,
             item: ModuleDefId,
         ) {
-            macro_rules! insert {
-                ($map:ident[$key:path].$insert:ident($id:ident)) => {{
-                    let loc = $id.lookup(db);
-                    if loc.id.file_id() == file_id {
-                        $map[$key].$insert(loc.source(db).value, $id)
-                    }
-                }};
-            }
             match item {
                 ModuleDefId::FunctionId(id) => {
                     insert_item_loc(db, map, file_id, id, keys::FUNCTION)
@@ -145,9 +131,13 @@ impl ChildBySource for ItemScope {
                     AdtId::EnumId(id) => insert_item_loc(db, map, file_id, id, keys::ENUM),
                 },
                 ModuleDefId::MacroId(id) => match id {
-                    MacroId::Macro2Id(id) => insert!(map[keys::MACRO2].insert(id)),
-                    MacroId::MacroRulesId(id) => insert!(map[keys::MACRO_RULES].insert(id)),
-                    MacroId::ProcMacroId(id) => insert!(map[keys::PROC_MACRO].insert(id)),
+                    MacroId::Macro2Id(id) => insert_item_loc(db, map, file_id, id, keys::MACRO2),
+                    MacroId::MacroRulesId(id) => {
+                        insert_item_loc(db, map, file_id, id, keys::MACRO_RULES)
+                    }
+                    MacroId::ProcMacroId(id) => {
+                        insert_item_loc(db, map, file_id, id, keys::PROC_MACRO)
+                    }
                 },
                 ModuleDefId::ModuleId(_)
                 | ModuleDefId::EnumVariantId(_)
@@ -207,6 +197,40 @@ impl ChildBySource for DefWithBodyId {
     }
 }
 
+impl ChildBySource for GenericDefId {
+    fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
+        let (gfile_id, generic_params_list) = self.file_id_and_params_of(db);
+        if gfile_id != file_id {
+            return;
+        }
+
+        let generic_params = db.generic_params(*self);
+        let mut toc_idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
+        let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
+
+        // For traits the first type index is `Self`, skip it.
+        if let GenericDefId::TraitId(_) = *self {
+            toc_idx_iter.next().unwrap(); // advance_by(1);
+        }
+
+        if let Some(generic_params_list) = generic_params_list {
+            for (local_id, ast_param) in
+                toc_idx_iter.zip(generic_params_list.type_or_const_params())
+            {
+                let id = TypeOrConstParamId { parent: *self, local_id };
+                match ast_param {
+                    ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
+                    ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
+                }
+            }
+            for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
+                let id = LifetimeParamId { parent: *self, local_id };
+                res[keys::LIFETIME_PARAM].insert(ast_param, id);
+            }
+        }
+    }
+}
+
 fn insert_item_loc<ID, N, Data>(
     db: &dyn DefDatabase,
     res: &mut DynMap,
@@ -224,3 +248,11 @@ fn insert_item_loc<ID, N, Data>(
         res[key].insert(loc.source(db).value, id)
     }
 }
+
+fn add_assoc_item(db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId, item: AssocItemId) {
+    match item {
+        AssocItemId::FunctionId(func) => insert_item_loc(db, res, file_id, func, keys::FUNCTION),
+        AssocItemId::ConstId(konst) => insert_item_loc(db, res, file_id, konst, keys::CONST),
+        AssocItemId::TypeAliasId(ty) => insert_item_loc(db, res, file_id, ty, keys::TYPE_ALIAS),
+    }
+}
diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs
index 5986b7df3d9..540f643ae7d 100644
--- a/crates/hir-def/src/data/adt.rs
+++ b/crates/hir-def/src/data/adt.rs
@@ -10,7 +10,7 @@ use hir_expand::{
     HirFileId, InFile,
 };
 use intern::Interned;
-use la_arena::{Arena, ArenaMap};
+use la_arena::Arena;
 use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
 use syntax::ast::{self, HasName, HasVisibility};
 use triomphe::Arc;
@@ -22,13 +22,11 @@ use crate::{
     lang_item::LangItem,
     lower::LowerCtx,
     nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
-    src::HasChildSource,
-    src::HasSource,
     trace::Trace,
     tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
     type_ref::TypeRef,
     visibility::RawVisibility,
-    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
+    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId,
 };
 
 /// Note that we use `StructData` for unions as well!
@@ -387,46 +385,6 @@ impl VariantData {
     }
 }
 
-impl HasChildSource<LocalFieldId> for VariantId {
-    type Value = Either<ast::TupleField, ast::RecordField>;
-
-    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
-        let item_tree;
-        let (src, fields, container) = match *self {
-            VariantId::EnumVariantId(it) => {
-                let lookup = it.lookup(db);
-                item_tree = lookup.id.item_tree(db);
-                (
-                    lookup.source(db).map(|it| it.kind()),
-                    &item_tree[lookup.id.value].fields,
-                    lookup.parent.lookup(db).container,
-                )
-            }
-            VariantId::StructId(it) => {
-                let lookup = it.lookup(db);
-                item_tree = lookup.id.item_tree(db);
-                (
-                    lookup.source(db).map(|it| it.kind()),
-                    &item_tree[lookup.id.value].fields,
-                    lookup.container,
-                )
-            }
-            VariantId::UnionId(it) => {
-                let lookup = it.lookup(db);
-                item_tree = lookup.id.item_tree(db);
-                (
-                    lookup.source(db).map(|it| it.kind()),
-                    &item_tree[lookup.id.value].fields,
-                    lookup.container,
-                )
-            }
-        };
-        let mut trace = Trace::new_for_map();
-        lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
-        src.with_value(trace.into_map())
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum StructKind {
     Tuple,
@@ -434,7 +392,7 @@ pub enum StructKind {
     Unit,
 }
 
-fn lower_struct(
+pub(crate) fn lower_struct(
     db: &dyn DefDatabase,
     trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
     ast: &InFile<ast::StructKind>,
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index 0edd5e61688..1d2c7c3a55f 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -3,31 +3,27 @@
 //! generic parameters. See also the `Generics` type and the `generics_of` query
 //! in rustc.
 
-use base_db::FileId;
 use either::Either;
 use hir_expand::{
     name::{AsName, Name},
-    ExpandResult, HirFileId, InFile,
+    ExpandResult,
 };
 use intern::Interned;
-use la_arena::{Arena, ArenaMap, Idx};
+use la_arena::{Arena, Idx};
 use once_cell::unsync::Lazy;
 use stdx::impl_from;
 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
 use triomphe::Arc;
 
 use crate::{
-    child_by_source::ChildBySource,
     db::DefDatabase,
-    dyn_map::{keys, DynMap},
     expander::Expander,
-    item_tree::ItemTree,
+    item_tree::{GenericsItemTreeNode, ItemTree},
     lower::LowerCtx,
     nameres::{DefMap, MacroSubNs},
-    src::{HasChildSource, HasSource},
     type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
-    AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
-    LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
+    AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup,
+    TypeOrConstParamId, TypeParamId,
 };
 
 /// Data about a generic type parameter (to a function, struct, impl, ...).
@@ -418,13 +414,18 @@ impl GenericParams {
                 })
             }
         };
-        macro_rules! id_to_generics {
-            ($id:ident) => {{
-                let id = $id.lookup(db).id;
-                let tree = id.item_tree(db);
-                let item = &tree[id.value];
-                enabled_params(&item.generic_params, &tree)
-            }};
+        fn id_to_generics<Id: GenericsItemTreeNode>(
+            db: &dyn DefDatabase,
+            id: impl for<'db> Lookup<
+                Database<'db> = dyn DefDatabase + 'db,
+                Data = impl ItemTreeLoc<Id = Id>,
+            >,
+            enabled_params: impl Fn(&Interned<GenericParams>, &ItemTree) -> Interned<GenericParams>,
+        ) -> Interned<GenericParams> {
+            let id = id.lookup(db).item_tree_id();
+            let tree = id.item_tree(db);
+            let item = &tree[id.value];
+            enabled_params(item.generic_params(), &tree)
         }
 
         match def {
@@ -457,13 +458,13 @@ impl GenericParams {
                     Interned::new(generic_params.finish())
                 }
             }
-            GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics!(id),
-            GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id),
-            GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id),
-            GenericDefId::TraitId(id) => id_to_generics!(id),
-            GenericDefId::TraitAliasId(id) => id_to_generics!(id),
-            GenericDefId::TypeAliasId(id) => id_to_generics!(id),
-            GenericDefId::ImplId(id) => id_to_generics!(id),
+            GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics(db, id, enabled_params),
+            GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics(db, id, enabled_params),
+            GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics(db, id, enabled_params),
+            GenericDefId::TraitId(id) => id_to_generics(db, id, enabled_params),
+            GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
+            GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
+            GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
             GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
                 Interned::new(GenericParams {
                     type_or_consts: Default::default(),
@@ -507,130 +508,3 @@ impl GenericParams {
         })
     }
 }
-
-fn file_id_and_params_of(
-    db: &dyn DefDatabase,
-    def: GenericDefId,
-) -> (HirFileId, Option<ast::GenericParamList>) {
-    match def {
-        GenericDefId::FunctionId(it) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::TypeAliasId(it) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None),
-        GenericDefId::AdtId(AdtId::StructId(it)) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
-        GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
-        // We won't be using this ID anyway
-        GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None),
-    }
-}
-
-fn file_id_and_params_of_item_loc<Loc>(
-    db: &dyn DefDatabase,
-    def: impl for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = Loc>,
-) -> (HirFileId, Option<ast::GenericParamList>)
-where
-    Loc: HasSource,
-    Loc::Value: HasGenericParams,
-{
-    let src = def.lookup(db).source(db);
-    (src.file_id, src.value.generic_param_list())
-}
-
-impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
-    type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
-    fn child_source(
-        &self,
-        db: &dyn DefDatabase,
-    ) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
-        let generic_params = db.generic_params(*self);
-        let mut idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
-
-        let (file_id, generic_params_list) = file_id_and_params_of(db, *self);
-
-        let mut params = ArenaMap::default();
-
-        // For traits and trait aliases the first type index is `Self`, we need to add it before
-        // the other params.
-        match *self {
-            GenericDefId::TraitId(id) => {
-                let trait_ref = id.lookup(db).source(db).value;
-                let idx = idx_iter.next().unwrap();
-                params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
-            }
-            GenericDefId::TraitAliasId(id) => {
-                let alias = id.lookup(db).source(db).value;
-                let idx = idx_iter.next().unwrap();
-                params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
-            }
-            _ => {}
-        }
-
-        if let Some(generic_params_list) = generic_params_list {
-            for (idx, ast_param) in idx_iter.zip(generic_params_list.type_or_const_params()) {
-                params.insert(idx, Either::Left(ast_param));
-            }
-        }
-
-        InFile::new(file_id, params)
-    }
-}
-
-impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
-    type Value = ast::LifetimeParam;
-    fn child_source(
-        &self,
-        db: &dyn DefDatabase,
-    ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
-        let generic_params = db.generic_params(*self);
-        let idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
-
-        let (file_id, generic_params_list) = file_id_and_params_of(db, *self);
-
-        let mut params = ArenaMap::default();
-
-        if let Some(generic_params_list) = generic_params_list {
-            for (idx, ast_param) in idx_iter.zip(generic_params_list.lifetime_params()) {
-                params.insert(idx, ast_param);
-            }
-        }
-
-        InFile::new(file_id, params)
-    }
-}
-
-impl ChildBySource for GenericDefId {
-    fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
-        let (gfile_id, generic_params_list) = file_id_and_params_of(db, *self);
-        if gfile_id != file_id {
-            return;
-        }
-
-        let generic_params = db.generic_params(*self);
-        let mut toc_idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
-        let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
-
-        // For traits the first type index is `Self`, skip it.
-        if let GenericDefId::TraitId(_) = *self {
-            toc_idx_iter.next().unwrap(); // advance_by(1);
-        }
-
-        if let Some(generic_params_list) = generic_params_list {
-            for (local_id, ast_param) in
-                toc_idx_iter.zip(generic_params_list.type_or_const_params())
-            {
-                let id = TypeOrConstParamId { parent: *self, local_id };
-                match ast_param {
-                    ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
-                    ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
-                }
-            }
-            for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
-                let id = LifetimeParamId { parent: *self, local_id };
-                res[keys::LIFETIME_PARAM].insert(ast_param, id);
-            }
-        }
-    }
-}
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 96b606ec1db..be16a5e31a2 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -347,6 +347,9 @@ pub trait ItemTreeNode: Clone {
     fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
     fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner;
 }
+pub trait GenericsItemTreeNode: ItemTreeNode {
+    fn generic_params(&self) -> &Interned<GenericParams>;
+}
 
 pub struct FileItemTreeId<N>(Idx<N>);
 
@@ -473,7 +476,7 @@ impl<N> Hash for ItemTreeId<N> {
 }
 
 macro_rules! mod_items {
-    ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
+    ( $( $typ:ident $(<$generic_params:ident>)? in $fld:ident -> $ast:ty ),+ $(,)? ) => {
         #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
         pub enum ModItem {
             $(
@@ -513,6 +516,14 @@ macro_rules! mod_items {
                     &self.data().$fld[index]
                 }
             }
+
+            $(
+                impl GenericsItemTreeNode for $typ {
+                    fn generic_params(&self) -> &Interned<GenericParams> {
+                        &self.$generic_params
+                    }
+                }
+            )?
         )+
     };
 }
@@ -521,16 +532,16 @@ mod_items! {
     Use in uses -> ast::Use,
     ExternCrate in extern_crates -> ast::ExternCrate,
     ExternBlock in extern_blocks -> ast::ExternBlock,
-    Function in functions -> ast::Fn,
-    Struct in structs -> ast::Struct,
-    Union in unions -> ast::Union,
-    Enum in enums -> ast::Enum,
+    Function<explicit_generic_params> in functions -> ast::Fn,
+    Struct<generic_params> in structs -> ast::Struct,
+    Union<generic_params> in unions -> ast::Union,
+    Enum<generic_params> in enums -> ast::Enum,
     Const in consts -> ast::Const,
     Static in statics -> ast::Static,
-    Trait in traits -> ast::Trait,
-    TraitAlias in trait_aliases -> ast::TraitAlias,
-    Impl in impls -> ast::Impl,
-    TypeAlias in type_aliases -> ast::TypeAlias,
+    Trait<generic_params> in traits -> ast::Trait,
+    TraitAlias<generic_params> in trait_aliases -> ast::TraitAlias,
+    Impl<generic_params> in impls -> ast::Impl,
+    TypeAlias<generic_params> in type_aliases -> ast::TypeAlias,
     Mod in mods -> ast::Module,
     MacroCall in macro_calls -> ast::MacroCall,
     MacroRules in macro_rules -> ast::MacroRules,
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 1cc81589094..e0aa3ae6123 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -658,7 +658,7 @@ impl<'a> Ctx<'a> {
 
     fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
         let vis =
-            RawVisibility::from_ast_with_span_map(self.db, item.visibility(), self.span_map());
+            RawVisibility::from_opt_ast_with_span_map(self.db, item.visibility(), self.span_map());
         self.data().vis.alloc(vis)
     }
 
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index df1f61ae5ff..589e57cb6b7 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -87,7 +87,7 @@ use hir_expand::{
 use item_tree::ExternBlock;
 use la_arena::Idx;
 use nameres::DefMap;
-use span::Span;
+use span::{FileId, Span};
 use stdx::impl_from;
 use syntax::{ast, AstNode};
 
@@ -103,114 +103,6 @@ use crate::{
     },
 };
 
-/// A `ModuleId` that is always a crate's root module.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct CrateRootModuleId {
-    krate: CrateId,
-}
-
-impl CrateRootModuleId {
-    pub fn def_map(&self, db: &dyn DefDatabase) -> Arc<DefMap> {
-        db.crate_def_map(self.krate)
-    }
-
-    pub fn krate(self) -> CrateId {
-        self.krate
-    }
-}
-
-impl PartialEq<ModuleId> for CrateRootModuleId {
-    fn eq(&self, other: &ModuleId) -> bool {
-        other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
-    }
-}
-impl PartialEq<CrateRootModuleId> for ModuleId {
-    fn eq(&self, other: &CrateRootModuleId) -> bool {
-        other == self
-    }
-}
-
-impl From<CrateRootModuleId> for ModuleId {
-    fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
-        ModuleId { krate, block: None, local_id: DefMap::ROOT }
-    }
-}
-
-impl From<CrateRootModuleId> for ModuleDefId {
-    fn from(value: CrateRootModuleId) -> Self {
-        ModuleDefId::ModuleId(value.into())
-    }
-}
-
-impl From<CrateId> for CrateRootModuleId {
-    fn from(krate: CrateId) -> Self {
-        CrateRootModuleId { krate }
-    }
-}
-
-impl TryFrom<ModuleId> for CrateRootModuleId {
-    type Error = ();
-
-    fn try_from(ModuleId { krate, block, local_id }: ModuleId) -> Result<Self, Self::Error> {
-        if block.is_none() && local_id == DefMap::ROOT {
-            Ok(CrateRootModuleId { krate })
-        } else {
-            Err(())
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct ModuleId {
-    krate: CrateId,
-    /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the
-    /// `BlockId` of that block expression. If `None`, this module is part of the crate-level
-    /// `DefMap` of `krate`.
-    block: Option<BlockId>,
-    /// The module's ID in its originating `DefMap`.
-    pub local_id: LocalModuleId,
-}
-
-impl ModuleId {
-    pub fn def_map(self, db: &dyn DefDatabase) -> Arc<DefMap> {
-        match self.block {
-            Some(block) => db.block_def_map(block),
-            None => db.crate_def_map(self.krate),
-        }
-    }
-
-    pub fn krate(self) -> CrateId {
-        self.krate
-    }
-
-    pub fn name(self, db: &dyn DefDatabase) -> Option<Name> {
-        let def_map = self.def_map(db);
-        let parent = def_map[self.local_id].parent?;
-        def_map[parent].children.iter().find_map(|(name, module_id)| {
-            if *module_id == self.local_id {
-                Some(name.clone())
-            } else {
-                None
-            }
-        })
-    }
-
-    pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> {
-        self.def_map(db).containing_module(self.local_id)
-    }
-
-    pub fn containing_block(self) -> Option<BlockId> {
-        self.block
-    }
-
-    pub fn is_block_module(self) -> bool {
-        self.block.is_some() && self.local_id == DefMap::ROOT
-    }
-}
-
-/// An ID of a module, **local** to a `DefMap`.
-pub type LocalModuleId = Idx<nameres::ModuleData>;
-
 #[derive(Debug)]
 pub struct ItemLoc<N: ItemTreeNode> {
     pub container: ModuleId,
@@ -323,35 +215,6 @@ impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
 impl_loc!(EnumLoc, id: Enum, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct EnumVariantId(salsa::InternId);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct EnumVariantLoc {
-    pub id: ItemTreeId<Variant>,
-    pub parent: EnumId,
-    pub index: u32,
-}
-impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant);
-impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct FieldId {
-    pub parent: VariantId,
-    pub local_id: LocalFieldId,
-}
-
-pub type LocalFieldId = Idx<data::adt::FieldData>;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct TupleId(pub u32);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct TupleFieldId {
-    pub tuple: TupleId,
-    pub index: u32,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ConstId(salsa::InternId);
 type ConstLoc = AssocItemLoc<Const>;
 impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
@@ -406,13 +269,16 @@ impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_e
 impl_loc!(ExternBlockLoc, id: ExternBlock, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum MacroExpander {
-    Declarative,
-    BuiltIn(BuiltinFnLikeExpander),
-    BuiltInAttr(BuiltinAttrExpander),
-    BuiltInDerive(BuiltinDeriveExpander),
-    BuiltInEager(EagerExpander),
+pub struct EnumVariantId(salsa::InternId);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct EnumVariantLoc {
+    pub id: ItemTreeId<Variant>,
+    pub parent: EnumId,
+    pub index: u32,
 }
+impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant);
+impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
 pub struct Macro2Id(salsa::InternId);
@@ -448,6 +314,14 @@ bitflags::bitflags! {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum MacroExpander {
+    Declarative,
+    BuiltIn(BuiltinFnLikeExpander),
+    BuiltInAttr(BuiltinAttrExpander),
+    BuiltInDerive(BuiltinDeriveExpander),
+    BuiltInEager(EagerExpander),
+}
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
 pub struct ProcMacroId(salsa::InternId);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -471,6 +345,146 @@ pub struct BlockLoc {
 }
 impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
 
+/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
+/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct ConstBlockId(salsa::InternId);
+impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct ConstBlockLoc {
+    /// The parent of the anonymous const block.
+    pub parent: DefWithBodyId,
+    /// The root expression of this const block in the parent body.
+    pub root: hir::ExprId,
+}
+
+/// A `ModuleId` that is always a crate's root module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CrateRootModuleId {
+    krate: CrateId,
+}
+
+impl CrateRootModuleId {
+    pub fn def_map(&self, db: &dyn DefDatabase) -> Arc<DefMap> {
+        db.crate_def_map(self.krate)
+    }
+
+    pub fn krate(self) -> CrateId {
+        self.krate
+    }
+}
+
+impl PartialEq<ModuleId> for CrateRootModuleId {
+    fn eq(&self, other: &ModuleId) -> bool {
+        other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
+    }
+}
+
+impl From<CrateId> for CrateRootModuleId {
+    fn from(krate: CrateId) -> Self {
+        CrateRootModuleId { krate }
+    }
+}
+
+impl TryFrom<ModuleId> for CrateRootModuleId {
+    type Error = ();
+
+    fn try_from(ModuleId { krate, block, local_id }: ModuleId) -> Result<Self, Self::Error> {
+        if block.is_none() && local_id == DefMap::ROOT {
+            Ok(CrateRootModuleId { krate })
+        } else {
+            Err(())
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct ModuleId {
+    krate: CrateId,
+    /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the
+    /// `BlockId` of that block expression. If `None`, this module is part of the crate-level
+    /// `DefMap` of `krate`.
+    block: Option<BlockId>,
+    /// The module's ID in its originating `DefMap`.
+    pub local_id: LocalModuleId,
+}
+
+impl ModuleId {
+    pub fn def_map(self, db: &dyn DefDatabase) -> Arc<DefMap> {
+        match self.block {
+            Some(block) => db.block_def_map(block),
+            None => db.crate_def_map(self.krate),
+        }
+    }
+
+    pub fn krate(self) -> CrateId {
+        self.krate
+    }
+
+    pub fn name(self, db: &dyn DefDatabase) -> Option<Name> {
+        let def_map = self.def_map(db);
+        let parent = def_map[self.local_id].parent?;
+        def_map[parent].children.iter().find_map(|(name, module_id)| {
+            if *module_id == self.local_id {
+                Some(name.clone())
+            } else {
+                None
+            }
+        })
+    }
+
+    pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> {
+        self.def_map(db).containing_module(self.local_id)
+    }
+
+    pub fn containing_block(self) -> Option<BlockId> {
+        self.block
+    }
+
+    pub fn is_block_module(self) -> bool {
+        self.block.is_some() && self.local_id == DefMap::ROOT
+    }
+}
+
+impl PartialEq<CrateRootModuleId> for ModuleId {
+    fn eq(&self, other: &CrateRootModuleId) -> bool {
+        other == self
+    }
+}
+
+impl From<CrateRootModuleId> for ModuleId {
+    fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
+        ModuleId { krate, block: None, local_id: DefMap::ROOT }
+    }
+}
+
+impl From<CrateRootModuleId> for ModuleDefId {
+    fn from(value: CrateRootModuleId) -> Self {
+        ModuleDefId::ModuleId(value.into())
+    }
+}
+
+/// An ID of a module, **local** to a `DefMap`.
+pub type LocalModuleId = Idx<nameres::ModuleData>;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct FieldId {
+    pub parent: VariantId,
+    pub local_id: LocalFieldId,
+}
+
+pub type LocalFieldId = Idx<data::adt::FieldData>;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TupleId(pub u32);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TupleFieldId {
+    pub tuple: TupleId,
+    pub index: u32,
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TypeOrConstParamId {
     pub parent: GenericDefId,
@@ -611,20 +625,6 @@ impl_from!(
     for ModuleDefId
 );
 
-/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
-/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
-#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
-pub struct ConstBlockId(salsa::InternId);
-impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
-
-#[derive(Debug, Hash, PartialEq, Eq, Clone)]
-pub struct ConstBlockLoc {
-    /// The parent of the anonymous const block.
-    pub parent: DefWithBodyId,
-    /// The root expression of this const block in the parent body.
-    pub root: hir::ExprId,
-}
-
 /// Something that holds types, required for the current const arg lowering implementation as they
 /// need to be able to query where they are defined.
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@@ -892,6 +892,39 @@ impl_from!(
     for GenericDefId
 );
 
+impl GenericDefId {
+    fn file_id_and_params_of(
+        self,
+        db: &dyn DefDatabase,
+    ) -> (HirFileId, Option<ast::GenericParamList>) {
+        fn file_id_and_params_of_item_loc<Loc>(
+            db: &dyn DefDatabase,
+            def: impl for<'db> Lookup<Database<'db> = dyn DefDatabase + 'db, Data = Loc>,
+        ) -> (HirFileId, Option<ast::GenericParamList>)
+        where
+            Loc: src::HasSource,
+            Loc::Value: ast::HasGenericParams,
+        {
+            let src = def.lookup(db).source(db);
+            (src.file_id, ast::HasGenericParams::generic_param_list(&src.value))
+        }
+
+        match self {
+            GenericDefId::FunctionId(it) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::TypeAliasId(it) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None),
+            GenericDefId::AdtId(AdtId::StructId(it)) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
+            GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
+            // We won't be using this ID anyway
+            GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None),
+        }
+    }
+}
+
 impl From<AssocItemId> for GenericDefId {
     fn from(item: AssocItemId) -> Self {
         match item {
@@ -1224,6 +1257,34 @@ impl HasModule for GenericDefId {
     }
 }
 
+impl HasModule for AttrDefId {
+    fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+        match self {
+            AttrDefId::ModuleId(it) => *it,
+            AttrDefId::FieldId(it) => it.parent.module(db),
+            AttrDefId::AdtId(it) => it.module(db),
+            AttrDefId::FunctionId(it) => it.module(db),
+            AttrDefId::EnumVariantId(it) => it.module(db),
+            AttrDefId::StaticId(it) => it.module(db),
+            AttrDefId::ConstId(it) => it.module(db),
+            AttrDefId::TraitId(it) => it.module(db),
+            AttrDefId::TraitAliasId(it) => it.module(db),
+            AttrDefId::TypeAliasId(it) => it.module(db),
+            AttrDefId::ImplId(it) => it.module(db),
+            AttrDefId::ExternBlockId(it) => it.module(db),
+            AttrDefId::GenericParamId(it) => match it {
+                GenericParamId::TypeParamId(it) => it.parent(),
+                GenericParamId::ConstParamId(it) => it.parent(),
+                GenericParamId::LifetimeParamId(it) => it.parent,
+            }
+            .module(db),
+            AttrDefId::MacroId(it) => it.module(db),
+            AttrDefId::ExternCrateId(it) => it.module(db),
+            AttrDefId::UseId(it) => it.module(db),
+        }
+    }
+}
+
 impl ModuleDefId {
     /// Returns the module containing `self` (or `self`, if `self` is itself a module).
     ///
@@ -1245,34 +1306,6 @@ impl ModuleDefId {
     }
 }
 
-impl AttrDefId {
-    pub fn krate(&self, db: &dyn DefDatabase) -> CrateId {
-        match *self {
-            AttrDefId::ModuleId(it) => it.krate,
-            AttrDefId::FieldId(it) => it.parent.krate(db),
-            AttrDefId::AdtId(it) => it.krate(db),
-            AttrDefId::FunctionId(it) => it.krate(db),
-            AttrDefId::EnumVariantId(it) => it.krate(db),
-            AttrDefId::StaticId(it) => it.krate(db),
-            AttrDefId::ConstId(it) => it.krate(db),
-            AttrDefId::TraitId(it) => it.krate(db),
-            AttrDefId::TraitAliasId(it) => it.krate(db),
-            AttrDefId::TypeAliasId(it) => it.krate(db),
-            AttrDefId::ImplId(it) => it.krate(db),
-            AttrDefId::ExternBlockId(it) => it.krate(db),
-            AttrDefId::GenericParamId(it) => match it {
-                GenericParamId::TypeParamId(it) => it.parent(),
-                GenericParamId::ConstParamId(it) => it.parent(),
-                GenericParamId::LifetimeParamId(it) => it.parent,
-            }
-            .krate(db),
-            AttrDefId::MacroId(it) => it.krate(db),
-            AttrDefId::ExternCrateId(it) => it.krate(db),
-            AttrDefId::UseId(it) => it.krate(db),
-        }
-    }
-}
-
 /// A helper trait for converting to MacroCallId
 pub trait AsMacroCall {
     fn as_call_id(
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 7a9c4ea0169..db47d743c5a 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -27,9 +27,9 @@ use crate::{
     visibility::{RawVisibility, Visibility},
     AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
     ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
-    ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
-    ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
-    TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
+    ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId,
+    MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId,
+    TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
 };
 
 #[derive(Debug, Clone)]
@@ -248,6 +248,7 @@ impl Resolver {
             RawVisibility::Public => Some(Visibility::Public),
         }
     }
+
     pub fn resolve_path_in_value_ns(
         &self,
         db: &dyn DefDatabase,
@@ -1014,13 +1015,13 @@ impl HasResolver for CrateRootModuleId {
 
 impl HasResolver for TraitId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
+        lookup_resolver(db, self).push_generic_params_scope(db, self.into())
     }
 }
 
 impl HasResolver for TraitAliasId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
+        lookup_resolver(db, self).push_generic_params_scope(db, self.into())
     }
 }
 
@@ -1036,25 +1037,25 @@ impl<T: Into<AdtId> + Copy> HasResolver for T {
 
 impl HasResolver for FunctionId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
+        lookup_resolver(db, self).push_generic_params_scope(db, self.into())
     }
 }
 
 impl HasResolver for ConstId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
 impl HasResolver for StaticId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
 impl HasResolver for TypeAliasId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
+        lookup_resolver(db, self).push_generic_params_scope(db, self.into())
     }
 }
 
@@ -1071,19 +1072,19 @@ impl HasResolver for ImplId {
 impl HasResolver for ExternBlockId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         // Same as parent's
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
 impl HasResolver for ExternCrateId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
 impl HasResolver for UseId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
@@ -1170,18 +1171,28 @@ impl HasResolver for MacroId {
 
 impl HasResolver for Macro2Id {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
 impl HasResolver for ProcMacroId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
 
 impl HasResolver for MacroRulesId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
-        self.lookup(db).container.resolver(db)
+        lookup_resolver(db, self)
     }
 }
+
+fn lookup_resolver<'db>(
+    db: &(dyn DefDatabase + 'db),
+    lookup: impl Lookup<
+        Database<'db> = dyn DefDatabase + 'db,
+        Data = impl ItemTreeLoc<Container = impl HasResolver>,
+    >,
+) -> Resolver {
+    lookup.lookup(db).container().resolver(db)
+}
diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs
index d820456b92c..4283f003f89 100644
--- a/crates/hir-def/src/src.rs
+++ b/crates/hir-def/src/src.rs
@@ -1,10 +1,15 @@
 //! Utilities for mapping between hir IDs and the surface syntax.
 
+use either::Either;
 use hir_expand::InFile;
 use la_arena::ArenaMap;
 use syntax::ast;
 
-use crate::{db::DefDatabase, item_tree::ItemTreeNode, ItemTreeLoc, Lookup, UseId};
+use crate::{
+    data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId,
+    ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, UseId,
+    VariantId,
+};
 
 pub trait HasSource {
     type Value;
@@ -49,3 +54,105 @@ impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
         )
     }
 }
+
+impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
+    type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
+    fn child_source(
+        &self,
+        db: &dyn DefDatabase,
+    ) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
+        let generic_params = db.generic_params(*self);
+        let mut idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
+
+        let (file_id, generic_params_list) = self.file_id_and_params_of(db);
+
+        let mut params = ArenaMap::default();
+
+        // For traits and trait aliases the first type index is `Self`, we need to add it before
+        // the other params.
+        match *self {
+            GenericDefId::TraitId(id) => {
+                let trait_ref = id.lookup(db).source(db).value;
+                let idx = idx_iter.next().unwrap();
+                params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
+            }
+            GenericDefId::TraitAliasId(id) => {
+                let alias = id.lookup(db).source(db).value;
+                let idx = idx_iter.next().unwrap();
+                params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
+            }
+            _ => {}
+        }
+
+        if let Some(generic_params_list) = generic_params_list {
+            for (idx, ast_param) in idx_iter.zip(generic_params_list.type_or_const_params()) {
+                params.insert(idx, Either::Left(ast_param));
+            }
+        }
+
+        InFile::new(file_id, params)
+    }
+}
+
+impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
+    type Value = ast::LifetimeParam;
+    fn child_source(
+        &self,
+        db: &dyn DefDatabase,
+    ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
+        let generic_params = db.generic_params(*self);
+        let idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
+
+        let (file_id, generic_params_list) = self.file_id_and_params_of(db);
+
+        let mut params = ArenaMap::default();
+
+        if let Some(generic_params_list) = generic_params_list {
+            for (idx, ast_param) in idx_iter.zip(generic_params_list.lifetime_params()) {
+                params.insert(idx, ast_param);
+            }
+        }
+
+        InFile::new(file_id, params)
+    }
+}
+
+impl HasChildSource<LocalFieldId> for VariantId {
+    type Value = Either<ast::TupleField, ast::RecordField>;
+
+    fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
+        let item_tree;
+        let (src, fields, container) = match *self {
+            VariantId::EnumVariantId(it) => {
+                let lookup = it.lookup(db);
+                item_tree = lookup.id.item_tree(db);
+                (
+                    lookup.source(db).map(|it| it.kind()),
+                    &item_tree[lookup.id.value].fields,
+                    lookup.parent.lookup(db).container,
+                )
+            }
+            VariantId::StructId(it) => {
+                let lookup = it.lookup(db);
+                item_tree = lookup.id.item_tree(db);
+                (
+                    lookup.source(db).map(|it| it.kind()),
+                    &item_tree[lookup.id.value].fields,
+                    lookup.container,
+                )
+            }
+            VariantId::UnionId(it) => {
+                let lookup = it.lookup(db);
+                item_tree = lookup.id.item_tree(db);
+                (
+                    lookup.source(db).map(|it| it.kind()),
+                    &item_tree[lookup.id.value].fields,
+                    lookup.container,
+                )
+            }
+        };
+        let mut trace = Trace::new_for_map();
+        lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
+        src.with_value(trace.into_map())
+    }
+}
diff --git a/crates/hir-def/src/trace.rs b/crates/hir-def/src/trace.rs
index 04d5b266194..da50ee8dc7a 100644
--- a/crates/hir-def/src/trace.rs
+++ b/crates/hir-def/src/trace.rs
@@ -11,6 +11,8 @@
 //! projections.
 use la_arena::{Arena, ArenaMap, Idx, RawIdx};
 
+// FIXME: This isn't really used anymore, at least not in a way where it does anything useful.
+// Check if we should get rid of this or make proper use of it instead.
 pub(crate) struct Trace<T, V> {
     arena: Option<Arena<T>>,
     map: Option<ArenaMap<Idx<T>, V>>,
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 7ed48237f4c..0f3fac1cecd 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -37,10 +37,14 @@ impl RawVisibility {
         db: &dyn DefDatabase,
         node: InFile<Option<ast::Visibility>>,
     ) -> RawVisibility {
+        let node = match node.transpose() {
+            None => return RawVisibility::private(),
+            Some(node) => node,
+        };
         Self::from_ast_with_span_map(db, node.value, db.span_map(node.file_id).as_ref())
     }
 
-    pub(crate) fn from_ast_with_span_map(
+    pub(crate) fn from_opt_ast_with_span_map(
         db: &dyn DefDatabase,
         node: Option<ast::Visibility>,
         span_map: SpanMapRef<'_>,
@@ -49,29 +53,28 @@ impl RawVisibility {
             None => return RawVisibility::private(),
             Some(node) => node,
         };
-        match node.kind() {
+        Self::from_ast_with_span_map(db, node, span_map)
+    }
+
+    fn from_ast_with_span_map(
+        db: &dyn DefDatabase,
+        node: ast::Visibility,
+        span_map: SpanMapRef<'_>,
+    ) -> RawVisibility {
+        let path = match node.kind() {
             ast::VisibilityKind::In(path) => {
                 let path = ModPath::from_src(db.upcast(), path, span_map);
-                let path = match path {
+                match path {
                     None => return RawVisibility::private(),
                     Some(path) => path,
-                };
-                RawVisibility::Module(path, VisibilityExplicitness::Explicit)
-            }
-            ast::VisibilityKind::PubCrate => {
-                let path = ModPath::from_kind(PathKind::Crate);
-                RawVisibility::Module(path, VisibilityExplicitness::Explicit)
-            }
-            ast::VisibilityKind::PubSuper => {
-                let path = ModPath::from_kind(PathKind::Super(1));
-                RawVisibility::Module(path, VisibilityExplicitness::Explicit)
-            }
-            ast::VisibilityKind::PubSelf => {
-                let path = ModPath::from_kind(PathKind::Super(0));
-                RawVisibility::Module(path, VisibilityExplicitness::Explicit)
+                }
             }
-            ast::VisibilityKind::Pub => RawVisibility::Public,
-        }
+            ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate),
+            ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
+            ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::Super(0)),
+            ast::VisibilityKind::Pub => return RawVisibility::Public,
+        };
+        RawVisibility::Module(path, VisibilityExplicitness::Explicit)
     }
 
     pub fn resolve(
@@ -94,6 +97,10 @@ pub enum Visibility {
 }
 
 impl Visibility {
+    pub(crate) fn is_visible_from_other_crate(self) -> bool {
+        matches!(self, Visibility::Public)
+    }
+
     #[tracing::instrument(skip_all)]
     pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
         let to_module = match self {
@@ -105,24 +112,33 @@ impl Visibility {
             return false;
         }
         let def_map = from_module.def_map(db);
-        self.is_visible_from_def_map(db, &def_map, from_module.local_id)
-    }
-
-    pub(crate) fn is_visible_from_other_crate(self) -> bool {
-        matches!(self, Visibility::Public)
+        Self::is_visible_from_def_map_(db, &def_map, to_module, from_module.local_id)
     }
 
     pub(crate) fn is_visible_from_def_map(
         self,
         db: &dyn DefDatabase,
         def_map: &DefMap,
-        mut from_module: LocalModuleId,
+        from_module: LocalModuleId,
     ) -> bool {
-        let mut to_module = match self {
+        let to_module = match self {
             Visibility::Module(m, _) => m,
             Visibility::Public => return true,
         };
+        // if they're not in the same crate, it can't be visible
+        if def_map.krate() != to_module.krate {
+            return false;
+        }
+        Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
+    }
 
+    fn is_visible_from_def_map_(
+        db: &dyn DefDatabase,
+        def_map: &DefMap,
+        mut to_module: ModuleId,
+        mut from_module: LocalModuleId,
+    ) -> bool {
+        debug_assert_eq!(to_module.krate, def_map.krate());
         // `to_module` might be the root module of a block expression. Those have the same
         // visibility as the containing module (even though no items are directly nameable from
         // there, getting this right is important for method resolution).
@@ -130,20 +146,25 @@ impl Visibility {
 
         // Additional complication: `to_module` might be in `from_module`'s `DefMap`, which we're
         // currently computing, so we must not call the `def_map` query for it.
-        let mut arc;
+        let def_map_block = def_map.block_id();
         loop {
-            let to_module_def_map =
-                if to_module.krate == def_map.krate() && to_module.block == def_map.block_id() {
+            match (to_module.block, def_map_block) {
+                // to_module is not a block, so there is no parent def map to use
+                (None, _) => (),
+                (Some(a), Some(b)) if a == b => {
                     cov_mark::hit!(is_visible_from_same_block_def_map);
-                    def_map
-                } else {
-                    arc = to_module.def_map(db);
-                    &arc
-                };
-            match to_module_def_map.parent() {
-                Some(parent) => to_module = parent,
-                None => break,
+                    if let Some(parent) = def_map.parent() {
+                        to_module = parent;
+                    }
+                }
+                _ => {
+                    if let Some(parent) = to_module.def_map(db).parent() {
+                        to_module = parent;
+                        continue;
+                    }
+                }
             }
+            break;
         }
 
         // from_module needs to be a descendant of to_module
@@ -176,30 +197,25 @@ impl Visibility {
     /// visible in unrelated modules).
     pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
         match (self, other) {
-            (Visibility::Module(_, _) | Visibility::Public, Visibility::Public)
-            | (Visibility::Public, Visibility::Module(_, _)) => Some(Visibility::Public),
-            (Visibility::Module(mod_a, vis_a), Visibility::Module(mod_b, vis_b)) => {
+            (_, Visibility::Public) | (Visibility::Public, _) => Some(Visibility::Public),
+            (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
                 if mod_a.krate != mod_b.krate {
                     return None;
                 }
 
-                let mut a_ancestors = iter::successors(Some(mod_a.local_id), |&m| {
-                    let parent_id = def_map[m].parent?;
-                    Some(parent_id)
-                });
-                let mut b_ancestors = iter::successors(Some(mod_b.local_id), |&m| {
-                    let parent_id = def_map[m].parent?;
-                    Some(parent_id)
-                });
+                let mut a_ancestors =
+                    iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
+                let mut b_ancestors =
+                    iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
 
                 if a_ancestors.any(|m| m == mod_b.local_id) {
                     // B is above A
-                    return Some(Visibility::Module(mod_b, vis_b));
+                    return Some(Visibility::Module(mod_b, expl_b));
                 }
 
                 if b_ancestors.any(|m| m == mod_a.local_id) {
                     // A is above B
-                    return Some(Visibility::Module(mod_a, vis_a));
+                    return Some(Visibility::Module(mod_a, expl_a));
                 }
 
                 None
@@ -208,7 +224,8 @@ impl Visibility {
     }
 }
 
-/// Whether the item was imported through `pub(crate) use` or just `use`.
+/// Whether the item was imported through an explicit `pub(crate) use` or just a `use` without
+/// visibility.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum VisibilityExplicitness {
     Explicit,
diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs
index 3179a632e26..e91e64c81b0 100644
--- a/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -86,4 +86,30 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn block_module_madness2() {
+        check_diagnostics(
+            r#"
+fn main() {
+    use crate as ForceParentBlockDefMap;
+    let strukt = {
+        use crate as ForceParentBlockDefMap;
+        {
+            pub struct Struct {
+                field: (),
+            }
+            {
+                use crate as ForceParentBlockDefMap;
+                {
+                    Struct { field: () }
+                }
+            }
+        }
+    };
+    strukt.field;
+}
+"#,
+        );
+    }
 }