diff --git a/Cargo.lock b/Cargo.lock
index a2b263cf2d8..49e96236c51 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -999,23 +999,23 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
 
 [[package]]
 name = "lsp-server"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3711e4d6f491dc9edc0f1df80e204f38206775ac92c1241e89b79229a850bc00"
+version = "0.7.3"
 dependencies = [
  "crossbeam-channel",
  "log",
+ "lsp-types",
  "serde",
  "serde_json",
 ]
 
 [[package]]
 name = "lsp-server"
-version = "0.7.2"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2"
 dependencies = [
  "crossbeam-channel",
  "log",
- "lsp-types",
  "serde",
  "serde_json",
 ]
@@ -1555,7 +1555,7 @@ dependencies = [
  "ide-ssr",
  "itertools",
  "load-cargo",
- "lsp-server 0.7.1",
+ "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "lsp-types",
  "mbe",
  "mimalloc",
diff --git a/Cargo.toml b/Cargo.toml
index f6a50bfa6b2..5eb59d6db11 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -86,7 +86,7 @@ proc-macro-test = { path = "./crates/proc-macro-test" }
 # In-tree crates that are published separately and follow semver. See lib/README.md
 line-index = { version = "0.1.0-pre.1" }
 la-arena = { version = "0.3.1" }
-lsp-server = { version = "0.7.1" }
+lsp-server = { version = "0.7.3" }
 
 # non-local crates
 smallvec = { version = "1.10.0", features = [
@@ -97,7 +97,8 @@ smallvec = { version = "1.10.0", features = [
 smol_str = "0.2.0"
 nohash-hasher = "0.2.0"
 text-size = "1.1.0"
-serde = { version = "1.0.156", features = ["derive"] }
+# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde
+serde = { version = "1.0.156, < 1.0.172", features = ["derive"] }
 serde_json = "1.0.96"
 triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
 # can't upgrade due to dashmap depending on 0.12.3 currently
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 323ee4260e4..aaac0fc3790 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -130,6 +130,7 @@ impl ChangeFixture {
         let mut default_crate_root: Option<FileId> = None;
         let mut default_target_data_layout: Option<String> = None;
         let mut default_cfg = CfgOptions::default();
+        let mut default_env = Env::new_for_test_fixture();
 
         let mut file_set = FileSet::default();
         let mut current_source_root_kind = SourceRootKind::Local;
@@ -200,6 +201,7 @@ impl ChangeFixture {
                 assert!(default_crate_root.is_none());
                 default_crate_root = Some(file_id);
                 default_cfg = meta.cfg;
+                default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
                 default_target_data_layout = meta.target_data_layout;
             }
 
@@ -220,7 +222,7 @@ impl ChangeFixture {
                 None,
                 default_cfg,
                 Default::default(),
-                Env::new_for_test_fixture(),
+                default_env,
                 false,
                 CrateOrigin::Local { repo: None, name: None },
                 default_target_data_layout
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index c47799f1320..b75c7079be7 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -686,6 +686,12 @@ impl fmt::Display for Edition {
     }
 }
 
+impl Extend<(String, String)> for Env {
+    fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
+        self.entries.extend(iter);
+    }
+}
+
 impl FromIterator<(String, String)> for Env {
     fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
         Env { entries: FromIterator::from_iter(iter) }
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index fae07111806..a5db75a91eb 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -431,12 +431,10 @@ impl AttrsWithOwner {
                         .item_tree(db)
                         .raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into()))
                         .clone(),
-                    ModuleOrigin::BlockExpr { block } => RawAttrs::from_attrs_owner(
-                        db.upcast(),
-                        InFile::new(block.file_id, block.to_node(db.upcast()))
-                            .as_ref()
-                            .map(|it| it as &dyn ast::HasAttrs),
-                    ),
+                    ModuleOrigin::BlockExpr { id, .. } => {
+                        let tree = db.block_item_tree_query(id);
+                        tree.raw_attrs(AttrOwner::TopLevel).clone()
+                    }
                 }
             }
             AttrDefId::FieldId(it) => {
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 3853a6ab3a5..7071fcb9394 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -505,6 +505,9 @@ impl ExprCollector<'_> {
                 let mut args = Vec::new();
                 let mut arg_types = Vec::new();
                 if let Some(pl) = e.param_list() {
+                    let num_params = pl.params().count();
+                    args.reserve_exact(num_params);
+                    arg_types.reserve_exact(num_params);
                     for param in pl.params() {
                         let pat = this.collect_pat_top(param.pat());
                         let type_ref =
@@ -1100,7 +1103,9 @@ impl ExprCollector<'_> {
                 ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
                 _ => false,
             });
-            statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+            statement_has_item
+                || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+                || (block.may_carry_attributes() && block.attrs().next().is_some())
         };
 
         let block_id = if block_has_items {
diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs
index 4e015a7fbbb..44eeed9e3fb 100644
--- a/crates/hir-def/src/body/tests/block.rs
+++ b/crates/hir-def/src/body/tests/block.rs
@@ -38,9 +38,9 @@ fn outer() {
 "#,
         expect![[r#"
             block scope
-            CrateStruct: t
-            PlainStruct: t v
-            SelfStruct: t
+            CrateStruct: ti
+            PlainStruct: ti vi
+            SelfStruct: ti
             Struct: v
             SuperStruct: _
 
@@ -66,7 +66,7 @@ fn outer() {
 "#,
         expect![[r#"
             block scope
-            imported: t v
+            imported: ti vi
             name: v
 
             crate
@@ -92,9 +92,9 @@ fn outer() {
 "#,
         expect![[r#"
             block scope
-            inner1: t
+            inner1: ti
             inner2: v
-            outer: v
+            outer: vi
 
             block scope
             inner: v
@@ -121,7 +121,7 @@ struct Struct {}
 "#,
         expect![[r#"
             block scope
-            Struct: t
+            Struct: ti
 
             crate
             Struct: t
@@ -153,7 +153,7 @@ fn outer() {
 "#,
         expect![[r#"
             block scope
-            ResolveMe: t
+            ResolveMe: ti
 
             block scope
             m2: t
@@ -214,7 +214,7 @@ fn f() {
 "#,
         expect![[r#"
             block scope
-            ResolveMe: t
+            ResolveMe: ti
 
             block scope
             h: v
@@ -292,7 +292,7 @@ pub mod cov_mark {
             nested: v
 
             crate
-            cov_mark: t
+            cov_mark: ti
             f: v
         "#]],
     );
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 91db68058b0..68defa3858f 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -487,7 +487,7 @@ impl ExternCrateDeclData {
             db.crate_def_map(loc.container.krate())
                 .extern_prelude()
                 .find(|&(prelude_name, ..)| *prelude_name == name)
-                .map(|(_, root)| root.krate())
+                .map(|(_, (root, _))| root.krate())
         };
 
         Arc::new(Self {
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index e34a6768f28..31c1a713031 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -82,6 +82,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     #[salsa::invoke(ItemTree::file_item_tree_query)]
     fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
 
+    #[salsa::invoke(ItemTree::block_item_tree_query)]
+    fn block_item_tree_query(&self, block_id: BlockId) -> Arc<ItemTree>;
+
     #[salsa::invoke(crate_def_map_wait)]
     #[salsa::transparent]
     fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index df2af4c89b0..b60e7909105 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -11,7 +11,7 @@ use crate::{
     nameres::DefMap,
     path::{ModPath, PathKind},
     visibility::Visibility,
-    ModuleDefId, ModuleId,
+    CrateRootModuleId, ModuleDefId, ModuleId,
 };
 
 /// Find a path that can be used to refer to a certain item. This can depend on
@@ -81,7 +81,7 @@ fn find_path_inner(
     }
 
     let def_map = from.def_map(db);
-    let crate_root = def_map.crate_root().into();
+    let crate_root = def_map.crate_root();
     // - if the item is a module, jump straight to module search
     if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
         let mut visited_modules = FxHashSet::default();
@@ -149,7 +149,7 @@ fn find_path_for_module(
     db: &dyn DefDatabase,
     def_map: &DefMap,
     visited_modules: &mut FxHashSet<ModuleId>,
-    crate_root: ModuleId,
+    crate_root: CrateRootModuleId,
     from: ModuleId,
     module_id: ModuleId,
     max_len: usize,
@@ -183,7 +183,7 @@ fn find_path_for_module(
 
     // - if the item is the crate root of a dependency crate, return the name from the extern prelude
     let root_def_map = crate_root.def_map(db);
-    for (name, def_id) in root_def_map.extern_prelude() {
+    for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() {
         if module_id == def_id {
             let name = scope_name.unwrap_or_else(|| name.clone());
 
@@ -192,7 +192,7 @@ fn find_path_for_module(
                     def_map[local_id]
                         .scope
                         .type_(&name)
-                        .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
+                        .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into()))
                 })
                 .is_some();
             let kind = if name_already_occupied_in_type_ns {
@@ -224,6 +224,7 @@ fn find_path_for_module(
     )
 }
 
+// FIXME: Do we still need this now that we record import origins, and hence aliases?
 fn find_in_scope(
     db: &dyn DefDatabase,
     def_map: &DefMap,
@@ -244,7 +245,7 @@ fn find_in_prelude(
     item: ItemInNs,
     from: ModuleId,
 ) -> Option<ModPath> {
-    let prelude_module = root_def_map.prelude()?;
+    let (prelude_module, _) = root_def_map.prelude()?;
     // Preludes in block DefMaps are ignored, only the crate DefMap is searched
     let prelude_def_map = prelude_module.def_map(db);
     let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
@@ -293,7 +294,7 @@ fn calculate_best_path(
     db: &dyn DefDatabase,
     def_map: &DefMap,
     visited_modules: &mut FxHashSet<ModuleId>,
-    crate_root: ModuleId,
+    crate_root: CrateRootModuleId,
     max_len: usize,
     item: ItemInNs,
     from: ModuleId,
@@ -346,6 +347,11 @@ fn calculate_best_path(
         let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
             let import_map = db.import_map(dep.crate_id);
             import_map.import_info_for(item).and_then(|info| {
+                if info.is_doc_hidden {
+                    // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
+                    return None;
+                }
+
                 // Determine best path for containing module and append last segment from `info`.
                 // FIXME: we should guide this to look up the path locally, or from the same crate again?
                 let mut path = find_path_for_module(
@@ -1293,4 +1299,65 @@ pub mod prelude {
             "None",
         );
     }
+
+    #[test]
+    fn different_crate_renamed_through_dep() {
+        check_found_path(
+            r#"
+//- /main.rs crate:main deps:intermediate
+$0
+//- /intermediate.rs crate:intermediate deps:std
+pub extern crate std as std_renamed;
+//- /std.rs crate:std
+pub struct S;
+    "#,
+            "intermediate::std_renamed::S",
+            "intermediate::std_renamed::S",
+            "intermediate::std_renamed::S",
+            "intermediate::std_renamed::S",
+        );
+    }
+
+    #[test]
+    fn different_crate_doc_hidden() {
+        check_found_path(
+            r#"
+//- /main.rs crate:main deps:intermediate
+$0
+//- /intermediate.rs crate:intermediate deps:std
+#[doc(hidden)]
+pub extern crate std;
+pub extern crate std as longer;
+//- /std.rs crate:std
+pub struct S;
+    "#,
+            "intermediate::longer::S",
+            "intermediate::longer::S",
+            "intermediate::longer::S",
+            "intermediate::longer::S",
+        );
+    }
+
+    #[test]
+    fn respect_doc_hidden() {
+        check_found_path(
+            r#"
+//- /main.rs crate:main deps:std,lazy_static
+$0
+//- /lazy_static.rs crate:lazy_static deps:core
+#[doc(hidden)]
+pub use core::ops::Deref as __Deref;
+//- /std.rs crate:std deps:core
+pub use core::ops;
+//- /core.rs crate:core
+pub mod ops {
+    pub trait Deref {}
+}
+    "#,
+            "std::ops::Deref",
+            "std::ops::Deref",
+            "std::ops::Deref",
+            "std::ops::Deref",
+        );
+    }
 }
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index d7d44e41388..1e2535a8a99 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -21,10 +21,11 @@ use crate::{
     db::DefDatabase,
     dyn_map::{keys, DynMap},
     expander::Expander,
+    item_tree::{AttrOwner, ItemTree},
     lower::LowerCtx,
     nameres::{DefMap, MacroSubNs},
     src::{HasChildSource, HasSource},
-    type_ref::{LifetimeRef, TypeBound, TypeRef},
+    type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
     AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
     LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
 };
@@ -48,7 +49,7 @@ pub struct LifetimeParamData {
 pub struct ConstParamData {
     pub name: Name,
     pub ty: Interned<TypeRef>,
-    pub has_default: bool,
+    pub default: Option<ConstRef>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -75,7 +76,7 @@ impl TypeOrConstParamData {
     pub fn has_default(&self) -> bool {
         match self {
             TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
-            TypeOrConstParamData::ConstParamData(it) => it.has_default,
+            TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
         }
     }
 
@@ -154,12 +155,58 @@ impl GenericParams {
         def: GenericDefId,
     ) -> Interned<GenericParams> {
         let _p = profile::span("generic_params_query");
+
+        let krate = def.module(db).krate;
+        let cfg_options = db.crate_graph();
+        let cfg_options = &cfg_options[krate].cfg_options;
+
+        // Returns the generic parameters that are enabled under the current `#[cfg]` options
+        let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| {
+            let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
+
+            // In the common case, no parameters will by disabled by `#[cfg]` attributes.
+            // Therefore, make a first pass to check if all parameters are enabled and, if so,
+            // clone the `Interned<GenericParams>` instead of recreating an identical copy.
+            let all_type_or_consts_enabled =
+                params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into()));
+            let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into()));
+
+            if all_type_or_consts_enabled && all_lifetimes_enabled {
+                params.clone()
+            } else {
+                Interned::new(GenericParams {
+                    type_or_consts: all_type_or_consts_enabled
+                        .then(|| params.type_or_consts.clone())
+                        .unwrap_or_else(|| {
+                            params
+                                .type_or_consts
+                                .iter()
+                                .filter_map(|(idx, param)| {
+                                    enabled(idx.into()).then(|| param.clone())
+                                })
+                                .collect()
+                        }),
+                    lifetimes: all_lifetimes_enabled
+                        .then(|| params.lifetimes.clone())
+                        .unwrap_or_else(|| {
+                            params
+                                .lifetimes
+                                .iter()
+                                .filter_map(|(idx, param)| {
+                                    enabled(idx.into()).then(|| param.clone())
+                                })
+                                .collect()
+                        }),
+                    where_predicates: params.where_predicates.clone(),
+                })
+            }
+        };
         macro_rules! id_to_generics {
             ($id:ident) => {{
                 let id = $id.lookup(db).id;
                 let tree = id.item_tree(db);
                 let item = &tree[id.value];
-                item.generic_params.clone()
+                enabled_params(&item.generic_params, &tree)
             }};
         }
 
@@ -169,7 +216,8 @@ impl GenericParams {
                 let tree = loc.id.item_tree(db);
                 let item = &tree[loc.id.value];
 
-                let mut generic_params = GenericParams::clone(&item.explicit_generic_params);
+                let enabled_params = enabled_params(&item.explicit_generic_params, &tree);
+                let mut generic_params = GenericParams::clone(&enabled_params);
 
                 let module = loc.container.module(db);
                 let func_data = db.function_data(id);
@@ -198,9 +246,14 @@ impl GenericParams {
         }
     }
 
-    pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) {
+    pub(crate) fn fill(
+        &mut self,
+        lower_ctx: &LowerCtx<'_>,
+        node: &dyn HasGenericParams,
+        add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
+    ) {
         if let Some(params) = node.generic_param_list() {
-            self.fill_params(lower_ctx, params)
+            self.fill_params(lower_ctx, params, add_param_attrs)
         }
         if let Some(where_clause) = node.where_clause() {
             self.fill_where_predicates(lower_ctx, where_clause);
@@ -218,7 +271,12 @@ impl GenericParams {
         }
     }
 
-    fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) {
+    fn fill_params(
+        &mut self,
+        lower_ctx: &LowerCtx<'_>,
+        params: ast::GenericParamList,
+        mut add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
+    ) {
         for type_or_const_param in params.type_or_const_params() {
             match type_or_const_param {
                 ast::TypeOrConstParam::Type(type_param) => {
@@ -232,13 +290,14 @@ impl GenericParams {
                         default,
                         provenance: TypeParamProvenance::TypeParamList,
                     };
-                    self.type_or_consts.alloc(param.into());
+                    let idx = self.type_or_consts.alloc(param.into());
                     let type_ref = TypeRef::Path(name.into());
                     self.fill_bounds(
                         lower_ctx,
                         type_param.type_bound_list(),
                         Either::Left(type_ref),
                     );
+                    add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param));
                 }
                 ast::TypeOrConstParam::Const(const_param) => {
                     let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -248,9 +307,10 @@ impl GenericParams {
                     let param = ConstParamData {
                         name,
                         ty: Interned::new(ty),
-                        has_default: const_param.default_val().is_some(),
+                        default: ConstRef::from_const_param(lower_ctx, &const_param),
                     };
-                    self.type_or_consts.alloc(param.into());
+                    let idx = self.type_or_consts.alloc(param.into());
+                    add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
                 }
             }
         }
@@ -258,13 +318,14 @@ impl GenericParams {
             let name =
                 lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(&lt));
             let param = LifetimeParamData { name: name.clone() };
-            self.lifetimes.alloc(param);
+            let idx = self.lifetimes.alloc(param);
             let lifetime_ref = LifetimeRef::new_name(name);
             self.fill_bounds(
                 lower_ctx,
                 lifetime_param.type_bound_list(),
                 Either::Right(lifetime_ref),
             );
+            add_param_attrs(idx.into(), ast::GenericParam::LifetimeParam(lifetime_param));
         }
     }
 
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index 57f023ef35d..75adf21abdc 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -393,6 +393,17 @@ impl ConstRef {
         Self::Scalar(LiteralConstRef::Unknown)
     }
 
+    pub(crate) fn from_const_param(
+        lower_ctx: &LowerCtx<'_>,
+        param: &ast::ConstParam,
+    ) -> Option<Self> {
+        let default = param.default_val();
+        match default {
+            Some(_) => Some(Self::from_const_arg(lower_ctx, default)),
+            None => None,
+        }
+    }
+
     pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
         struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
         impl fmt::Display for Display<'_> {
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs
index 4b2e5041a12..6038caaf8fc 100644
--- a/crates/hir-def/src/import_map.rs
+++ b/crates/hir-def/src/import_map.rs
@@ -11,6 +11,7 @@ use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
 use triomphe::Arc;
 
+use crate::item_scope::ImportOrExternCrate;
 use crate::{
     db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
     ModuleDefId, ModuleId, TraitId,
@@ -29,6 +30,8 @@ pub struct ImportInfo {
     pub container: ModuleId,
     /// Whether the import is a trait associated item or not.
     pub is_trait_assoc_item: bool,
+    /// Whether this item is annotated with `#[doc(hidden)]`.
+    pub is_doc_hidden: bool,
 }
 
 /// A map from publicly exported items to its name.
@@ -109,23 +112,41 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
         });
 
         for (name, per_ns) in visible_items {
-            for item in per_ns.iter_items() {
+            for (item, import) in per_ns.iter_items() {
+                // FIXME: Not yet used, but will be once we handle doc(hidden) import sources
+                let attr_id = if let Some(import) = import {
+                    match import {
+                        ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
+                        ImportOrExternCrate::Import(id) => Some(id.import.into()),
+                    }
+                } else {
+                    match item {
+                        ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(),
+                        ItemInNs::Macros(id) => Some(id.into()),
+                    }
+                };
+                let is_doc_hidden =
+                    attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden());
+
                 let import_info = ImportInfo {
                     name: name.clone(),
                     container: module,
                     is_trait_assoc_item: false,
+                    is_doc_hidden,
                 };
 
                 match depth_map.entry(item) {
-                    Entry::Vacant(entry) => {
-                        entry.insert(depth);
-                    }
+                    Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)),
                     Entry::Occupied(mut entry) => {
-                        if depth < *entry.get() {
-                            entry.insert(depth);
-                        } else {
+                        let &(occ_depth, occ_is_doc_hidden) = entry.get();
+                        // Prefer the one that is not doc(hidden),
+                        // Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one.
+                        let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden
+                            || occ_is_doc_hidden == is_doc_hidden && depth < occ_depth;
+                        if !overwrite_entry {
                             continue;
                         }
+                        entry.insert((depth, is_doc_hidden));
                     }
                 }
 
@@ -162,10 +183,10 @@ fn collect_trait_assoc_items(
     trait_import_info: &ImportInfo,
 ) {
     let _p = profile::span("collect_trait_assoc_items");
-    for (assoc_item_name, item) in &db.trait_data(tr).items {
+    for &(ref assoc_item_name, item) in &db.trait_data(tr).items {
         let module_def_id = match item {
-            AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
-            AssocItemId::ConstId(c) => ModuleDefId::from(*c),
+            AssocItemId::FunctionId(f) => ModuleDefId::from(f),
+            AssocItemId::ConstId(c) => ModuleDefId::from(c),
             // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
             // qualifier, ergo no need to store it for imports in import_map
             AssocItemId::TypeAliasId(_) => {
@@ -183,6 +204,7 @@ fn collect_trait_assoc_items(
             container: trait_import_info.container,
             name: assoc_item_name.clone(),
             is_trait_assoc_item: true,
+            is_doc_hidden: db.attrs(item.into()).has_doc_hidden(),
         };
         map.insert(assoc_item, assoc_item_info);
     }
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 873accafb43..7c11fb9d136 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -6,6 +6,7 @@ use std::collections::hash_map::Entry;
 use base_db::CrateId;
 use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
 use itertools::Itertools;
+use la_arena::Idx;
 use once_cell::sync::Lazy;
 use profile::Count;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -15,16 +16,10 @@ use syntax::ast;
 
 use crate::{
     db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId,
-    ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
-    UseId,
+    ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId,
+    TraitId, UseId,
 };
 
-#[derive(Copy, Clone, Debug)]
-pub(crate) enum ImportType {
-    Glob,
-    Named,
-}
-
 #[derive(Debug, Default)]
 pub struct PerNsGlobImports {
     types: FxHashSet<(LocalModuleId, Name)>,
@@ -32,15 +27,50 @@ pub struct PerNsGlobImports {
     macros: FxHashSet<(LocalModuleId, Name)>,
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImportOrExternCrate {
+    Import(ImportId),
+    ExternCrate(ExternCrateId),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub(crate) enum ImportType {
+    Import(ImportId),
+    Glob(UseId),
+    ExternCrate(ExternCrateId),
+}
+
+impl ImportOrExternCrate {
+    pub fn into_import(self) -> Option<ImportId> {
+        match self {
+            ImportOrExternCrate::Import(it) => Some(it),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImportOrDef {
+    Import(ImportId),
+    ExternCrate(ExternCrateId),
+    Def(ModuleDefId),
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub struct ImportId {
+    pub import: UseId,
+    pub idx: Idx<ast::UseTree>,
+}
+
 #[derive(Debug, Default, PartialEq, Eq)]
 pub struct ItemScope {
     _c: Count<Self>,
 
     /// Defs visible in this scope. This includes `declarations`, but also
-    /// imports.
-    types: FxHashMap<Name, (ModuleDefId, Visibility)>,
-    values: FxHashMap<Name, (ModuleDefId, Visibility)>,
-    macros: FxHashMap<Name, (MacroId, Visibility)>,
+    /// imports. The imports belong to this module and can be resolved by using them on
+    /// the `use_imports_*` fields.
+    types: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
+    values: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>,
+    macros: FxHashMap<Name, (MacroId, Visibility, Option<ImportId>)>,
     unresolved: FxHashSet<Name>,
 
     /// The defs declared in this scope. Each def has a single scope where it is
@@ -50,7 +80,14 @@ pub struct ItemScope {
     impls: Vec<ImplId>,
     unnamed_consts: Vec<ConstId>,
     /// Traits imported via `use Trait as _;`.
-    unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
+    unnamed_trait_imports: FxHashMap<TraitId, (Visibility, Option<ImportId>)>,
+
+    // the resolutions of the imports of this scope
+    use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
+    use_imports_values: FxHashMap<ImportId, ImportOrDef>,
+    use_imports_macros: FxHashMap<ImportId, ImportOrDef>,
+
+    use_decls: Vec<UseId>,
     extern_crate_decls: Vec<ExternCrateId>,
     /// Macros visible in current module in legacy textual scope
     ///
@@ -82,7 +119,7 @@ struct DeriveMacroInvocation {
 pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
     BuiltinType::ALL
         .iter()
-        .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public)))
+        .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None)))
         .collect()
 });
 
@@ -105,11 +142,77 @@ impl ItemScope {
             .chain(self.values.keys())
             .chain(self.macros.keys())
             .chain(self.unresolved.iter())
-            .sorted()
             .unique()
+            .sorted()
             .map(move |name| (name, self.get(name)))
     }
 
+    pub fn imports(&self) -> impl Iterator<Item = ImportId> + '_ {
+        self.use_imports_types
+            .keys()
+            .copied()
+            .filter_map(ImportOrExternCrate::into_import)
+            .chain(self.use_imports_values.keys().copied())
+            .chain(self.use_imports_macros.keys().copied())
+            .unique()
+            .sorted()
+    }
+
+    pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs {
+        let mut res = PerNs::none();
+
+        let mut def_map;
+        let mut scope = self;
+        while let Some(&m) = scope.use_imports_macros.get(&import) {
+            match m {
+                ImportOrDef::Import(i) => {
+                    let module_id = i.import.lookup(db).container;
+                    def_map = module_id.def_map(db);
+                    scope = &def_map[module_id.local_id].scope;
+                    import = i;
+                }
+                ImportOrDef::Def(ModuleDefId::MacroId(def)) => {
+                    res.macros = Some((def, Visibility::Public, None));
+                    break;
+                }
+                _ => break,
+            }
+        }
+        let mut scope = self;
+        while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) {
+            match m {
+                ImportOrDef::Import(i) => {
+                    let module_id = i.import.lookup(db).container;
+                    def_map = module_id.def_map(db);
+                    scope = &def_map[module_id.local_id].scope;
+                    import = i;
+                }
+                ImportOrDef::Def(def) => {
+                    res.types = Some((def, Visibility::Public, None));
+                    break;
+                }
+                _ => break,
+            }
+        }
+        let mut scope = self;
+        while let Some(&m) = scope.use_imports_values.get(&import) {
+            match m {
+                ImportOrDef::Import(i) => {
+                    let module_id = i.import.lookup(db).container;
+                    def_map = module_id.def_map(db);
+                    scope = &def_map[module_id.local_id].scope;
+                    import = i;
+                }
+                ImportOrDef::Def(def) => {
+                    res.values = Some((def, Visibility::Public, None));
+                    break;
+                }
+                _ => break,
+            }
+        }
+        res
+    }
+
     pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
         self.declarations.iter().copied()
     }
@@ -121,8 +224,7 @@ impl ItemScope {
     }
 
     pub fn use_decls(&self) -> impl Iterator<Item = UseId> + ExactSizeIterator + '_ {
-        // FIXME: to be implemented
-        std::iter::empty()
+        self.use_decls.iter().copied()
     }
 
     pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
@@ -132,13 +234,13 @@ impl ItemScope {
     pub fn values(
         &self,
     ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
-        self.values.values().copied()
+        self.values.values().copied().map(|(a, b, _)| (a, b))
     }
 
-    pub fn types(
+    pub(crate) fn types(
         &self,
     ) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
-        self.types.values().copied()
+        self.types.values().copied().map(|(def, vis, _)| (def, vis))
     }
 
     pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
@@ -165,33 +267,55 @@ impl ItemScope {
     }
 
     pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> {
-        self.types.get(name).copied()
+        self.types.get(name).copied().map(|(a, b, _)| (a, b))
     }
 
     /// XXX: this is O(N) rather than O(1), try to not introduce new usages.
     pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
-        let (def, mut iter) = match item {
-            ItemInNs::Macros(def) => {
-                return self.macros.iter().find_map(|(name, &(other_def, vis))| {
-                    (other_def == def).then_some((name, vis))
-                });
-            }
-            ItemInNs::Types(def) => (def, self.types.iter()),
-            ItemInNs::Values(def) => (def, self.values.iter()),
-        };
-        iter.find_map(|(name, &(other_def, vis))| (other_def == def).then_some((name, vis)))
+        match item {
+            ItemInNs::Macros(def) => self
+                .macros
+                .iter()
+                .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+            ItemInNs::Types(def) => self
+                .types
+                .iter()
+                .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+
+            ItemInNs::Values(def) => self
+                .values
+                .iter()
+                .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+        }
     }
 
     pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ {
         self.types
             .values()
-            .filter_map(|&(def, _)| match def {
+            .filter_map(|&(def, _, _)| match def {
                 ModuleDefId::TraitId(t) => Some(t),
                 _ => None,
             })
             .chain(self.unnamed_trait_imports.keys().copied())
     }
 
+    pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
+        self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
+            self.unnamed_trait_imports.iter().map(|(tr, (vis, i))| {
+                (
+                    None,
+                    PerNs::types(
+                        ModuleDefId::TraitId(*tr),
+                        *vis,
+                        i.map(ImportOrExternCrate::Import),
+                    ),
+                )
+            }),
+        )
+    }
+}
+
+impl ItemScope {
     pub(crate) fn declare(&mut self, def: ModuleDefId) {
         self.declarations.push(def)
     }
@@ -277,12 +401,14 @@ impl ItemScope {
         })
     }
 
+    // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
     pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
-        self.unnamed_trait_imports.get(&tr).copied()
+        self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a)
     }
 
     pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
-        self.unnamed_trait_imports.insert(tr, vis);
+        // FIXME: import
+        self.unnamed_trait_imports.insert(tr, (vis, None));
     }
 
     pub(crate) fn push_res_with_import(
@@ -290,51 +416,187 @@ impl ItemScope {
         glob_imports: &mut PerNsGlobImports,
         lookup: (LocalModuleId, Name),
         def: PerNs,
-        def_import_type: ImportType,
+        import: Option<ImportType>,
     ) -> bool {
         let mut changed = false;
 
-        macro_rules! check_changed {
-            (
-                $changed:ident,
-                ( $this:ident / $def:ident ) . $field:ident,
-                $glob_imports:ident [ $lookup:ident ],
-                $def_import_type:ident
-            ) => {{
-                if let Some(fld) = $def.$field {
-                    let existing = $this.$field.entry($lookup.1.clone());
-                    match existing {
-                        Entry::Vacant(entry) => {
-                            match $def_import_type {
-                                ImportType::Glob => {
-                                    $glob_imports.$field.insert($lookup.clone());
-                                }
-                                ImportType::Named => {
-                                    $glob_imports.$field.remove(&$lookup);
-                                }
-                            }
+        // FIXME: Document and simplify this
 
-                            entry.insert(fld);
-                            $changed = true;
+        if let Some(mut fld) = def.types {
+            let existing = self.types.entry(lookup.1.clone());
+            match existing {
+                Entry::Vacant(entry) => {
+                    match import {
+                        Some(ImportType::Glob(_)) => {
+                            glob_imports.types.insert(lookup.clone());
                         }
-                        Entry::Occupied(mut entry)
-                            if matches!($def_import_type, ImportType::Named) =>
-                        {
-                            if $glob_imports.$field.remove(&$lookup) {
-                                cov_mark::hit!(import_shadowed);
-                                entry.insert(fld);
-                                $changed = true;
+                        _ => _ = glob_imports.types.remove(&lookup),
+                    }
+                    let import = match import {
+                        Some(ImportType::ExternCrate(extern_crate)) => {
+                            Some(ImportOrExternCrate::ExternCrate(extern_crate))
+                        }
+                        Some(ImportType::Import(import)) => {
+                            Some(ImportOrExternCrate::Import(import))
+                        }
+                        None | Some(ImportType::Glob(_)) => None,
+                    };
+                    let prev = std::mem::replace(&mut fld.2, import);
+                    if let Some(import) = import {
+                        self.use_imports_types.insert(
+                            import,
+                            match prev {
+                                Some(ImportOrExternCrate::Import(import)) => {
+                                    ImportOrDef::Import(import)
+                                }
+                                Some(ImportOrExternCrate::ExternCrate(import)) => {
+                                    ImportOrDef::ExternCrate(import)
+                                }
+                                None => ImportOrDef::Def(fld.0),
+                            },
+                        );
+                    }
+                    entry.insert(fld);
+                    changed = true;
+                }
+                Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+                    if glob_imports.types.remove(&lookup) {
+                        let import = match import {
+                            Some(ImportType::ExternCrate(extern_crate)) => {
+                                Some(ImportOrExternCrate::ExternCrate(extern_crate))
                             }
+                            Some(ImportType::Import(import)) => {
+                                Some(ImportOrExternCrate::Import(import))
+                            }
+                            None | Some(ImportType::Glob(_)) => None,
+                        };
+                        let prev = std::mem::replace(&mut fld.2, import);
+                        if let Some(import) = import {
+                            self.use_imports_types.insert(
+                                import,
+                                match prev {
+                                    Some(ImportOrExternCrate::Import(import)) => {
+                                        ImportOrDef::Import(import)
+                                    }
+                                    Some(ImportOrExternCrate::ExternCrate(import)) => {
+                                        ImportOrDef::ExternCrate(import)
+                                    }
+                                    None => ImportOrDef::Def(fld.0),
+                                },
+                            );
                         }
-                        _ => {}
+                        cov_mark::hit!(import_shadowed);
+                        entry.insert(fld);
+                        changed = true;
                     }
                 }
-            }};
+                _ => {}
+            }
         }
 
-        check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type);
-        check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type);
-        check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type);
+        if let Some(mut fld) = def.values {
+            let existing = self.values.entry(lookup.1.clone());
+            match existing {
+                Entry::Vacant(entry) => {
+                    match import {
+                        Some(ImportType::Glob(_)) => {
+                            glob_imports.values.insert(lookup.clone());
+                        }
+                        _ => _ = glob_imports.values.remove(&lookup),
+                    }
+                    let import = match import {
+                        Some(ImportType::Import(import)) => Some(import),
+                        _ => None,
+                    };
+                    let prev = std::mem::replace(&mut fld.2, import);
+                    if let Some(import) = import {
+                        self.use_imports_values.insert(
+                            import,
+                            match prev {
+                                Some(import) => ImportOrDef::Import(import),
+                                None => ImportOrDef::Def(fld.0),
+                            },
+                        );
+                    }
+                    entry.insert(fld);
+                    changed = true;
+                }
+                Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+                    if glob_imports.values.remove(&lookup) {
+                        cov_mark::hit!(import_shadowed);
+                        let import = match import {
+                            Some(ImportType::Import(import)) => Some(import),
+                            _ => None,
+                        };
+                        let prev = std::mem::replace(&mut fld.2, import);
+                        if let Some(import) = import {
+                            self.use_imports_values.insert(
+                                import,
+                                match prev {
+                                    Some(import) => ImportOrDef::Import(import),
+                                    None => ImportOrDef::Def(fld.0),
+                                },
+                            );
+                        }
+                        entry.insert(fld);
+                        changed = true;
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        if let Some(mut fld) = def.macros {
+            let existing = self.macros.entry(lookup.1.clone());
+            match existing {
+                Entry::Vacant(entry) => {
+                    match import {
+                        Some(ImportType::Glob(_)) => {
+                            glob_imports.macros.insert(lookup.clone());
+                        }
+                        _ => _ = glob_imports.macros.remove(&lookup),
+                    }
+                    let import = match import {
+                        Some(ImportType::Import(import)) => Some(import),
+                        _ => None,
+                    };
+                    let prev = std::mem::replace(&mut fld.2, import);
+                    if let Some(import) = import {
+                        self.use_imports_macros.insert(
+                            import,
+                            match prev {
+                                Some(import) => ImportOrDef::Import(import),
+                                None => ImportOrDef::Def(fld.0.into()),
+                            },
+                        );
+                    }
+                    entry.insert(fld);
+                    changed = true;
+                }
+                Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+                    if glob_imports.macros.remove(&lookup) {
+                        cov_mark::hit!(import_shadowed);
+                        let import = match import {
+                            Some(ImportType::Import(import)) => Some(import),
+                            _ => None,
+                        };
+                        let prev = std::mem::replace(&mut fld.2, import);
+                        if let Some(import) = import {
+                            self.use_imports_macros.insert(
+                                import,
+                                match prev {
+                                    Some(import) => ImportOrDef::Import(import),
+                                    None => ImportOrDef::Def(fld.0.into()),
+                                },
+                            );
+                        }
+                        entry.insert(fld);
+                        changed = true;
+                    }
+                }
+                _ => {}
+            }
+        }
 
         if def.is_none() && self.unresolved.insert(lookup.1) {
             changed = true;
@@ -343,27 +605,18 @@ impl ItemScope {
         changed
     }
 
-    pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
-        self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
-            self.unnamed_trait_imports
-                .iter()
-                .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))),
-        )
-    }
-
     /// Marks everything that is not a procedural macro as private to `this_module`.
     pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
         self.types
             .values_mut()
-            .chain(self.values.values_mut())
+            .map(|(def, vis, _)| (def, vis))
+            .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis)))
             .map(|(_, v)| v)
-            .chain(self.unnamed_trait_imports.values_mut())
+            .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis))
             .for_each(|vis| *vis = Visibility::Module(this_module));
 
-        for (mac, vis) in self.macros.values_mut() {
-            if let MacroId::ProcMacroId(_) = mac {
-                // FIXME: Technically this is insufficient since reexports of proc macros are also
-                // forbidden. Practically nobody does that.
+        for (mac, vis, import) in self.macros.values_mut() {
+            if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) {
                 continue;
             }
 
@@ -382,14 +635,25 @@ impl ItemScope {
                 name.map_or("_".to_string(), |name| name.display(db).to_string())
             );
 
-            if def.types.is_some() {
+            if let Some((.., i)) = def.types {
                 buf.push_str(" t");
+                match i {
+                    Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
+                    Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
+                    None => (),
+                }
             }
-            if def.values.is_some() {
+            if let Some((.., i)) = def.values {
                 buf.push_str(" v");
+                if i.is_some() {
+                    buf.push('i');
+                }
             }
-            if def.macros.is_some() {
+            if let Some((.., i)) = def.macros {
                 buf.push_str(" m");
+                if i.is_some() {
+                    buf.push('i');
+                }
             }
             if def.is_none() {
                 buf.push_str(" _");
@@ -415,10 +679,17 @@ impl ItemScope {
             attr_macros,
             derive_macros,
             extern_crate_decls,
+            use_decls,
+            use_imports_values,
+            use_imports_types,
+            use_imports_macros,
         } = self;
         types.shrink_to_fit();
         values.shrink_to_fit();
         macros.shrink_to_fit();
+        use_imports_types.shrink_to_fit();
+        use_imports_values.shrink_to_fit();
+        use_imports_macros.shrink_to_fit();
         unresolved.shrink_to_fit();
         declarations.shrink_to_fit();
         impls.shrink_to_fit();
@@ -428,32 +699,44 @@ impl ItemScope {
         attr_macros.shrink_to_fit();
         derive_macros.shrink_to_fit();
         extern_crate_decls.shrink_to_fit();
+        use_decls.shrink_to_fit();
     }
 }
 
 impl PerNs {
-    pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs {
+    pub(crate) fn from_def(
+        def: ModuleDefId,
+        v: Visibility,
+        has_constructor: bool,
+        import: Option<ImportOrExternCrate>,
+    ) -> PerNs {
         match def {
-            ModuleDefId::ModuleId(_) => PerNs::types(def, v),
-            ModuleDefId::FunctionId(_) => PerNs::values(def, v),
+            ModuleDefId::ModuleId(_) => PerNs::types(def, v, import),
+            ModuleDefId::FunctionId(_) => {
+                PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
+            }
             ModuleDefId::AdtId(adt) => match adt {
-                AdtId::UnionId(_) => PerNs::types(def, v),
-                AdtId::EnumId(_) => PerNs::types(def, v),
+                AdtId::UnionId(_) => PerNs::types(def, v, import),
+                AdtId::EnumId(_) => PerNs::types(def, v, import),
                 AdtId::StructId(_) => {
                     if has_constructor {
-                        PerNs::both(def, def, v)
+                        PerNs::both(def, def, v, import)
                     } else {
-                        PerNs::types(def, v)
+                        PerNs::types(def, v, import)
                     }
                 }
             },
-            ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
-            ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
-            ModuleDefId::TraitId(_) => PerNs::types(def, v),
-            ModuleDefId::TraitAliasId(_) => PerNs::types(def, v),
-            ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
-            ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
-            ModuleDefId::MacroId(mac) => PerNs::macros(mac, v),
+            ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import),
+            ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => {
+                PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
+            }
+            ModuleDefId::TraitId(_) => PerNs::types(def, v, import),
+            ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
+            ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
+            ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
+            ModuleDefId::MacroId(mac) => {
+                PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import))
+            }
         }
     }
 }
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index c9b0f75f1a8..3e1922750b9 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -64,11 +64,11 @@ use triomphe::Arc;
 use crate::{
     attr::Attrs,
     db::DefDatabase,
-    generics::GenericParams,
+    generics::{GenericParams, LifetimeParamData, TypeOrConstParamData},
     path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
     type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
     visibility::RawVisibility,
-    BlockId,
+    BlockId, Lookup,
 };
 
 #[derive(Copy, Clone, Eq, PartialEq)]
@@ -143,6 +143,16 @@ impl ItemTree {
         Arc::new(item_tree)
     }
 
+    pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
+        let loc = block.lookup(db);
+        let block = loc.ast_id.to_node(db.upcast());
+
+        let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
+        let mut item_tree = ctx.lower_block(&block);
+        item_tree.shrink_to_fit();
+        Arc::new(item_tree)
+    }
+
     /// Returns an iterator over all items located at the top level of the `HirFileId` this
     /// `ItemTree` was created from.
     pub fn top_level_items(&self) -> &[ModItem] {
@@ -178,13 +188,6 @@ impl ItemTree {
         self.data.get_or_insert_with(Box::default)
     }
 
-    fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
-        let loc = db.lookup_intern_block(block);
-        let block = loc.ast_id.to_node(db.upcast());
-        let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
-        Arc::new(ctx.lower_block(&block))
-    }
-
     fn shrink_to_fit(&mut self) {
         if let Some(data) = &mut self.data {
             let ItemTreeData {
@@ -296,10 +299,12 @@ pub enum AttrOwner {
     Variant(Idx<Variant>),
     Field(Idx<Field>),
     Param(Idx<Param>),
+    TypeOrConstParamData(Idx<TypeOrConstParamData>),
+    LifetimeParamData(Idx<LifetimeParamData>),
 }
 
 macro_rules! from_attrs {
-    ( $( $var:ident($t:ty) ),+ ) => {
+    ( $( $var:ident($t:ty) ),+ $(,)? ) => {
         $(
             impl From<$t> for AttrOwner {
                 fn from(t: $t) -> AttrOwner {
@@ -310,7 +315,14 @@ macro_rules! from_attrs {
     };
 }
 
-from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>));
+from_attrs!(
+    ModItem(ModItem),
+    Variant(Idx<Variant>),
+    Field(Idx<Field>),
+    Param(Idx<Param>),
+    TypeOrConstParamData(Idx<TypeOrConstParamData>),
+    LifetimeParamData(Idx<LifetimeParamData>),
+);
 
 /// Trait implemented by all item nodes in the item tree.
 pub trait ItemTreeNode: Clone {
@@ -373,7 +385,7 @@ impl TreeId {
 
     pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
         match self.block {
-            Some(block) => ItemTree::block_item_tree(db, block),
+            Some(block) => db.block_item_tree_query(block),
             None => db.file_item_tree(self.file),
         }
     }
@@ -761,6 +773,19 @@ impl Use {
             lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
         source_map[index].clone()
     }
+    /// Maps a `UseTree` contained in this import back to its AST node.
+    pub fn use_tree_source_map(
+        &self,
+        db: &dyn DefDatabase,
+        file_id: HirFileId,
+    ) -> Arena<ast::UseTree> {
+        // Re-lower the AST item and get the source map.
+        // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
+        let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
+        let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
+        let hygiene = Hygiene::new(db.upcast(), file_id);
+        lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree").1
+    }
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -785,7 +810,7 @@ impl UseTree {
     fn expand_impl(
         &self,
         prefix: Option<ModPath>,
-        cb: &mut dyn FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
+        cb: &mut impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
     ) {
         fn concat_mod_paths(
             prefix: Option<ModPath>,
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 7b898e62dba..e4702c113b8 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -77,6 +77,9 @@ impl<'a> Ctx<'a> {
     }
 
     pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
+        self.tree
+            .attrs
+            .insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.hygiene()));
         self.tree.top_level = block
             .statements()
             .filter_map(|stmt| match stmt {
@@ -602,7 +605,21 @@ impl<'a> Ctx<'a> {
             generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
         }
 
-        generics.fill(&self.body_ctx, node);
+        let add_param_attrs = |item, param| {
+            let attrs = RawAttrs::new(self.db.upcast(), &param, self.body_ctx.hygiene());
+            // This is identical to the body of `Ctx::add_attrs()` but we can't call that here
+            // because it requires `&mut self` and the call to `generics.fill()` below also
+            // references `self`.
+            match self.tree.attrs.entry(item) {
+                Entry::Occupied(mut entry) => {
+                    *entry.get_mut() = entry.get().merge(attrs);
+                }
+                Entry::Vacant(entry) => {
+                    entry.insert(attrs);
+                }
+            }
+        };
+        generics.fill(&self.body_ctx, node, add_param_attrs);
 
         generics.shrink_to_fit();
         Interned::new(generics)
@@ -763,7 +780,7 @@ impl UseTreeLowering<'_> {
     }
 }
 
-pub(super) fn lower_use_tree(
+pub(crate) fn lower_use_tree(
     db: &dyn DefDatabase,
     hygiene: &Hygiene,
     tree: ast::UseTree,
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index da30830fe45..4b852dd613e 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -16,7 +16,7 @@ pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> Strin
     let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
 
     if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
-        p.print_attrs(attrs, true);
+        p.print_attrs(attrs, true, "\n");
     }
     p.blank();
 
@@ -84,22 +84,23 @@ impl Printer<'_> {
         }
     }
 
-    fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) {
+    fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) {
         let inner = if inner { "!" } else { "" };
         for attr in &**attrs {
-            wln!(
+            w!(
                 self,
-                "#{}[{}{}]",
+                "#{}[{}{}]{}",
                 inner,
                 attr.path.display(self.db),
                 attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
+                separated_by,
             );
         }
     }
 
-    fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) {
+    fn print_attrs_of(&mut self, of: impl Into<AttrOwner>, separated_by: &str) {
         if let Some(attrs) = self.tree.attrs.get(&of.into()) {
-            self.print_attrs(attrs, false);
+            self.print_attrs(attrs, false, separated_by);
         }
     }
 
@@ -118,7 +119,7 @@ impl Printer<'_> {
                 self.indented(|this| {
                     for field in fields.clone() {
                         let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
-                        this.print_attrs_of(field);
+                        this.print_attrs_of(field, "\n");
                         this.print_visibility(*visibility);
                         w!(this, "{}: ", name.display(self.db));
                         this.print_type_ref(type_ref);
@@ -132,7 +133,7 @@ impl Printer<'_> {
                 self.indented(|this| {
                     for field in fields.clone() {
                         let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
-                        this.print_attrs_of(field);
+                        this.print_attrs_of(field, "\n");
                         this.print_visibility(*visibility);
                         w!(this, "{}: ", name.display(self.db));
                         this.print_type_ref(type_ref);
@@ -195,7 +196,7 @@ impl Printer<'_> {
     }
 
     fn print_mod_item(&mut self, item: ModItem) {
-        self.print_attrs_of(item);
+        self.print_attrs_of(item, "\n");
 
         match item {
             ModItem::Use(it) => {
@@ -261,7 +262,7 @@ impl Printer<'_> {
                 if !params.is_empty() {
                     self.indented(|this| {
                         for param in params.clone() {
-                            this.print_attrs_of(param);
+                            this.print_attrs_of(param, "\n");
                             match &this.tree[param] {
                                 Param::Normal(ty) => {
                                     if flags.contains(FnFlags::HAS_SELF_PARAM) {
@@ -319,7 +320,7 @@ impl Printer<'_> {
                 self.indented(|this| {
                     for variant in variants.clone() {
                         let Variant { name, fields, ast_id: _ } = &this.tree[variant];
-                        this.print_attrs_of(variant);
+                        this.print_attrs_of(variant, "\n");
                         w!(this, "{}", name.display(self.db));
                         this.print_fields(fields);
                         wln!(this, ",");
@@ -484,11 +485,12 @@ impl Printer<'_> {
 
         w!(self, "<");
         let mut first = true;
-        for (_, lt) in params.lifetimes.iter() {
+        for (idx, lt) in params.lifetimes.iter() {
             if !first {
                 w!(self, ", ");
             }
             first = false;
+            self.print_attrs_of(idx, " ");
             w!(self, "{}", lt.name.display(self.db));
         }
         for (idx, x) in params.type_or_consts.iter() {
@@ -496,6 +498,7 @@ impl Printer<'_> {
                 w!(self, ", ");
             }
             first = false;
+            self.print_attrs_of(idx, " ");
             match x {
                 TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
                     Some(name) => w!(self, "{}", name.display(self.db)),
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 5ded4b6b273..4180f817209 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -358,3 +358,15 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
         "#]],
     )
 }
+
+#[test]
+fn generics_with_attributes() {
+    check(
+        r#"
+struct S<#[cfg(never)] T>;
+        "#,
+        expect![[r#"
+            pub(self) struct S<#[cfg(never)] T>;
+        "#]],
+    )
+}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 1901db8a0f9..3f87fe62b83 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -109,6 +109,17 @@ impl CrateRootModuleId {
     }
 }
 
+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 }
@@ -854,14 +865,36 @@ impl_from!(
     ConstId,
     FunctionId,
     TraitId,
+    TraitAliasId,
     TypeAliasId,
     MacroId(Macro2Id, MacroRulesId, ProcMacroId),
     ImplId,
     GenericParamId,
-    ExternCrateId
+    ExternCrateId,
+    UseId
     for AttrDefId
 );
 
+impl TryFrom<ModuleDefId> for AttrDefId {
+    type Error = ();
+
+    fn try_from(value: ModuleDefId) -> Result<Self, Self::Error> {
+        match value {
+            ModuleDefId::ModuleId(it) => Ok(it.into()),
+            ModuleDefId::FunctionId(it) => Ok(it.into()),
+            ModuleDefId::AdtId(it) => Ok(it.into()),
+            ModuleDefId::EnumVariantId(it) => Ok(it.into()),
+            ModuleDefId::ConstId(it) => Ok(it.into()),
+            ModuleDefId::StaticId(it) => Ok(it.into()),
+            ModuleDefId::TraitId(it) => Ok(it.into()),
+            ModuleDefId::TypeAliasId(it) => Ok(it.into()),
+            ModuleDefId::TraitAliasId(id) => Ok(id.into()),
+            ModuleDefId::MacroId(id) => Ok(id.into()),
+            ModuleDefId::BuiltinType(_) => Err(()),
+        }
+    }
+}
+
 impl From<ItemContainerId> for AttrDefId {
     fn from(acid: ItemContainerId) -> Self {
         match acid {
@@ -872,6 +905,15 @@ impl From<ItemContainerId> for AttrDefId {
         }
     }
 }
+impl From<AssocItemId> for AttrDefId {
+    fn from(assoc: AssocItemId) -> Self {
+        match assoc {
+            AssocItemId::FunctionId(it) => AttrDefId::FunctionId(it),
+            AssocItemId::ConstId(it) => AttrDefId::ConstId(it),
+            AssocItemId::TypeAliasId(it) => AttrDefId::TypeAliasId(it),
+        }
+    }
+}
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum VariantId {
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 1250cbb742c..b232651db96 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -238,7 +238,7 @@ fn main() {
     /* error: expected expression */;
     /* error: expected expression, expected COMMA */;
     /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]);
-    /* error: expected expression, expected expression */;
+    /* error: expected expression, expected R_PAREN */;
     ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]);
 }
 "##]],
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index d8e4a4dcc7c..97554f93f1c 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -909,3 +909,68 @@ macro_rules! with_std {
 "##]],
     )
 }
+
+#[test]
+fn eager_regression_15403() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+    format_args /* +errors */ !("{}", line.1.);
+}
+
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+    /* error: expected field name or number *//* parse error: expected field name or number */
+::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]);
+}
+
+"##]],
+    );
+}
+
+#[test]
+fn eager_regression_154032() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+    format_args /* +errors */ !("{}", &[0 2]);
+}
+
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+    /* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */
+/* parse error: expected R_BRACK */
+/* parse error: expected COMMA */
+/* parse error: expected COMMA */
+/* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* parse error: expected R_PAREN */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]);
+}
+
+"##]],
+    );
+}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 7a87e61c693..8adced4e082 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -131,7 +131,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
             .as_call_id_with_errors(&db, krate, |path| {
                 resolver
                     .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
-                    .map(|it| macro_id_to_def_id(&db, it))
+                    .map(|(it, _)| macro_id_to_def_id(&db, it))
             })
             .unwrap();
         let macro_call_id = res.value.unwrap();
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 86818ce26dd..f2110410980 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -60,7 +60,7 @@ mod tests;
 use std::{cmp::Ord, ops::Deref};
 
 use base_db::{CrateId, Edition, FileId, ProcMacroKind};
-use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
+use hir_expand::{ast_id_map::FileAstId, name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
 use itertools::Itertools;
 use la_arena::Arena;
 use profile::Count;
@@ -77,8 +77,8 @@ use crate::{
     path::ModPath,
     per_ns::PerNs,
     visibility::Visibility,
-    AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
-    MacroId, ModuleId, ProcMacroId,
+    AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup,
+    MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
 };
 
 /// Contains the results of (early) name resolution.
@@ -105,10 +105,11 @@ pub struct DefMap {
     /// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used,
     /// but that attribute is nightly and when used in a block, it affects resolution globally
     /// so we aren't handling this correctly anyways).
-    prelude: Option<ModuleId>,
+    prelude: Option<(ModuleId, Option<UseId>)>,
     /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
     /// this contains all kinds of macro, not just `macro_rules!` macro.
-    macro_use_prelude: FxHashMap<Name, MacroId>,
+    /// ExternCrateId being None implies it being imported from the general prelude import.
+    macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>,
 
     /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
     /// attributes.
@@ -125,7 +126,7 @@ pub struct DefMap {
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct DefMapCrateData {
     /// The extern prelude which contains all root modules of external crates that are in scope.
-    extern_prelude: FxHashMap<Name, CrateRootModuleId>,
+    extern_prelude: FxHashMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
 
     /// Side table for resolving derive helpers.
     exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -217,16 +218,17 @@ pub enum ModuleOrigin {
     /// Note that non-inline modules, by definition, live inside non-macro file.
     File {
         is_mod_rs: bool,
-        declaration: AstId<ast::Module>,
+        declaration: FileAstId<ast::Module>,
         declaration_tree_id: ItemTreeId<Mod>,
         definition: FileId,
     },
     Inline {
         definition_tree_id: ItemTreeId<Mod>,
-        definition: AstId<ast::Module>,
+        definition: FileAstId<ast::Module>,
     },
     /// Pseudo-module introduced by a block scope (contains only inner items).
     BlockExpr {
+        id: BlockId,
         block: AstId<ast::BlockExpr>,
     },
 }
@@ -234,8 +236,12 @@ pub enum ModuleOrigin {
 impl ModuleOrigin {
     pub fn declaration(&self) -> Option<AstId<ast::Module>> {
         match self {
-            ModuleOrigin::File { declaration: module, .. }
-            | ModuleOrigin::Inline { definition: module, .. } => Some(*module),
+            &ModuleOrigin::File { declaration, declaration_tree_id, .. } => {
+                Some(AstId::new(declaration_tree_id.file_id(), declaration))
+            }
+            &ModuleOrigin::Inline { definition, definition_tree_id } => {
+                Some(AstId::new(definition_tree_id.file_id(), definition))
+            }
             ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None,
         }
     }
@@ -260,16 +266,17 @@ impl ModuleOrigin {
     /// That is, a file or a `mod foo {}` with items.
     fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
         match self {
-            ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
-                let file_id = *definition;
-                let sf = db.parse(file_id).tree();
-                InFile::new(file_id.into(), ModuleSource::SourceFile(sf))
+            &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
+                let sf = db.parse(definition).tree();
+                InFile::new(definition.into(), ModuleSource::SourceFile(sf))
             }
-            ModuleOrigin::Inline { definition, .. } => InFile::new(
-                definition.file_id,
-                ModuleSource::Module(definition.to_node(db.upcast())),
+            &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
+                definition_tree_id.file_id(),
+                ModuleSource::Module(
+                    AstId::new(definition_tree_id.file_id(), definition).to_node(db.upcast()),
+                ),
             ),
-            ModuleOrigin::BlockExpr { block } => {
+            ModuleOrigin::BlockExpr { block, .. } => {
                 InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast())))
             }
         }
@@ -314,9 +321,7 @@ impl DefMap {
     }
 
     pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
-        let block: BlockLoc = db.lookup_intern_block(block_id);
-
-        let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
+        let block: BlockLoc = block_id.lookup(db);
 
         let parent_map = block.module.def_map(db);
         let krate = block.module.krate;
@@ -325,8 +330,10 @@ impl DefMap {
         // modules declared by blocks with items. At the moment, we don't use
         // this visibility for anything outside IDE, so that's probably OK.
         let visibility = Visibility::Module(ModuleId { krate, local_id, block: None });
-        let module_data =
-            ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
+        let module_data = ModuleData::new(
+            ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id },
+            visibility,
+        );
 
         let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
         def_map.data = parent_map.data.clone();
@@ -338,7 +345,8 @@ impl DefMap {
             },
         });
 
-        let def_map = collector::collect_defs(db, def_map, tree_id);
+        let def_map =
+            collector::collect_defs(db, def_map, TreeId::new(block.ast_id.file_id, Some(block_id)));
         Arc::new(def_map)
     }
 
@@ -427,15 +435,19 @@ impl DefMap {
         self.block.map(|block| block.block)
     }
 
-    pub(crate) fn prelude(&self) -> Option<ModuleId> {
+    pub(crate) fn prelude(&self) -> Option<(ModuleId, Option<UseId>)> {
         self.prelude
     }
 
-    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
-        self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+    pub(crate) fn extern_prelude(
+        &self,
+    ) -> impl Iterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_ {
+        self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
     }
 
-    pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+    pub(crate) fn macro_use_prelude(
+        &self,
+    ) -> impl Iterator<Item = (&Name, (MacroId, Option<ExternCrateId>))> + '_ {
         self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
     }
 
@@ -638,8 +650,8 @@ impl ModuleData {
             ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
                 definition.into()
             }
-            ModuleOrigin::Inline { definition, .. } => definition.file_id,
-            ModuleOrigin::BlockExpr { block } => block.file_id,
+            ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id.file_id(),
+            ModuleOrigin::BlockExpr { block, .. } => block.file_id,
         }
     }
 
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index eef54fc492e..e9e71a8747f 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -33,7 +33,7 @@ use crate::{
     attr_macro_as_call_id,
     db::DefDatabase,
     derive_macro_as_call_id,
-    item_scope::{ImportType, PerNsGlobImports},
+    item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
     item_tree::{
         self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
         MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
@@ -52,10 +52,10 @@ use crate::{
     tt,
     visibility::{RawVisibility, Visibility},
     AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
-    ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
-    LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc,
-    ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc,
-    TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseLoc,
+    ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern,
+    ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
+    MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc,
+    StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc,
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -146,8 +146,8 @@ impl PartialResolvedImport {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 enum ImportSource {
-    Use { id: ItemTreeId<item_tree::Use>, use_tree: Idx<ast::UseTree> },
-    ExternCrate(ItemTreeId<item_tree::ExternCrate>),
+    Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind },
+    ExternCrate { id: ExternCrateId },
 }
 
 #[derive(Debug, Eq, PartialEq)]
@@ -155,54 +155,41 @@ struct Import {
     path: ModPath,
     alias: Option<ImportAlias>,
     visibility: RawVisibility,
-    kind: ImportKind,
     source: ImportSource,
-    is_prelude: bool,
-    is_macro_use: bool,
 }
 
 impl Import {
     fn from_use(
-        db: &dyn DefDatabase,
-        krate: CrateId,
         tree: &ItemTree,
-        id: ItemTreeId<item_tree::Use>,
+        item_tree_id: ItemTreeId<item_tree::Use>,
+        id: UseId,
+        is_prelude: bool,
         mut cb: impl FnMut(Self),
     ) {
-        let it = &tree[id.value];
-        let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+        let it = &tree[item_tree_id.value];
         let visibility = &tree[it.visibility];
-        let is_prelude = attrs.by_key("prelude_import").exists();
         it.use_tree.expand(|idx, path, kind, alias| {
             cb(Self {
                 path,
                 alias,
                 visibility: visibility.clone(),
-                kind,
-                is_prelude,
-                is_macro_use: false,
-                source: ImportSource::Use { id, use_tree: idx },
+                source: ImportSource::Use { use_tree: idx, id, is_prelude, kind },
             });
         });
     }
 
     fn from_extern_crate(
-        db: &dyn DefDatabase,
-        krate: CrateId,
         tree: &ItemTree,
-        id: ItemTreeId<item_tree::ExternCrate>,
+        item_tree_id: ItemTreeId<item_tree::ExternCrate>,
+        id: ExternCrateId,
     ) -> Self {
-        let it = &tree[id.value];
-        let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+        let it = &tree[item_tree_id.value];
         let visibility = &tree[it.visibility];
         Self {
             path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
             alias: it.alias.clone(),
             visibility: visibility.clone(),
-            kind: ImportKind::Plain,
-            is_prelude: false,
-            is_macro_use: attrs.by_key("macro_use").exists(),
-            source: ImportSource::ExternCrate(id),
+            source: ImportSource::ExternCrate { id },
         }
     }
 }
@@ -235,7 +222,7 @@ struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
     def_map: DefMap,
     deps: FxHashMap<Name, Dependency>,
-    glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
+    glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
     unresolved_imports: Vec<ImportDirective>,
     indeterminate_imports: Vec<ImportDirective>,
     unresolved_macros: Vec<MacroDirective>,
@@ -280,7 +267,7 @@ impl DefCollector<'_> {
             if dep.is_prelude() {
                 crate_data
                     .extern_prelude
-                    .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+                    .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
             }
         }
 
@@ -556,8 +543,12 @@ impl DefCollector<'_> {
             self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
 
         match per_ns.types {
-            Some((ModuleDefId::ModuleId(m), _)) => {
-                self.def_map.prelude = Some(m);
+            Some((ModuleDefId::ModuleId(m), _, import)) => {
+                // FIXME: This should specifically look for a glob import somehow and record that here
+                self.def_map.prelude = Some((
+                    m,
+                    import.and_then(ImportOrExternCrate::into_import).map(|it| it.import),
+                ));
             }
             types => {
                 tracing::debug!(
@@ -657,9 +648,9 @@ impl DefCollector<'_> {
             self.def_map.modules[module_id].scope.declare(macro_.into());
             self.update(
                 module_id,
-                &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+                &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
                 Visibility::Public,
-                ImportType::Named,
+                None,
             );
         }
     }
@@ -693,9 +684,9 @@ impl DefCollector<'_> {
         self.def_map.modules[module_id].scope.declare(macro_.into());
         self.update(
             module_id,
-            &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+            &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
             vis,
-            ImportType::Named,
+            None,
         );
     }
 
@@ -708,9 +699,9 @@ impl DefCollector<'_> {
         self.def_map.modules[module_id].scope.declare(macro_.into());
         self.update(
             module_id,
-            &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+            &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
             Visibility::Public,
-            ImportType::Named,
+            None,
         );
     }
 
@@ -720,21 +711,29 @@ impl DefCollector<'_> {
     /// Exported macros are just all macros in the root module scope.
     /// Note that it contains not only all `#[macro_export]` macros, but also all aliases
     /// created by `use` in the root module, ignoring the visibility of `use`.
-    fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
+    fn import_macros_from_extern_crate(
+        &mut self,
+        krate: CrateId,
+        names: Option<Vec<Name>>,
+        extern_crate: Option<ExternCrateId>,
+    ) {
         let def_map = self.db.crate_def_map(krate);
         // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
         // macros.
         let root_scope = &def_map[DefMap::ROOT].scope;
-        if let Some(names) = names {
-            for name in names {
-                // FIXME: Report diagnostic on 404.
-                if let Some(def) = root_scope.get(&name).take_macros() {
-                    self.def_map.macro_use_prelude.insert(name, def);
+        match names {
+            Some(names) => {
+                for name in names {
+                    // FIXME: Report diagnostic on 404.
+                    if let Some(def) = root_scope.get(&name).take_macros() {
+                        self.def_map.macro_use_prelude.insert(name, (def, extern_crate));
+                    }
                 }
             }
-        } else {
-            for (name, def) in root_scope.macros() {
-                self.def_map.macro_use_prelude.insert(name.clone(), def);
+            None => {
+                for (name, def) in root_scope.macros() {
+                    self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate));
+                }
             }
         }
     }
@@ -771,48 +770,53 @@ impl DefCollector<'_> {
         let _p = profile::span("resolve_import")
             .detail(|| format!("{}", import.path.display(self.db.upcast())));
         tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
-        if matches!(import.source, ImportSource::ExternCrate { .. }) {
-            let name = import
-                .path
-                .as_ident()
-                .expect("extern crate should have been desugared to one-element path");
+        match import.source {
+            ImportSource::ExternCrate { .. } => {
+                let name = import
+                    .path
+                    .as_ident()
+                    .expect("extern crate should have been desugared to one-element path");
 
-            let res = self.resolve_extern_crate(name);
+                let res = self.resolve_extern_crate(name);
 
-            match res {
-                Some(res) => {
-                    PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public))
-                }
-                None => PartialResolvedImport::Unresolved,
-            }
-        } else {
-            let res = self.def_map.resolve_path_fp_with_macro(
-                self.db,
-                ResolveMode::Import,
-                module_id,
-                &import.path,
-                BuiltinShadowMode::Module,
-                None, // An import may resolve to any kind of macro.
-            );
-
-            let def = res.resolved_def;
-            if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
-                return PartialResolvedImport::Unresolved;
-            }
-
-            if let Some(krate) = res.krate {
-                if krate != self.def_map.krate {
-                    return PartialResolvedImport::Resolved(
-                        def.filter_visibility(|v| matches!(v, Visibility::Public)),
-                    );
+                match res {
+                    Some(res) => PartialResolvedImport::Resolved(PerNs::types(
+                        res.into(),
+                        Visibility::Public,
+                        None,
+                    )),
+                    None => PartialResolvedImport::Unresolved,
                 }
             }
+            ImportSource::Use { .. } => {
+                let res = self.def_map.resolve_path_fp_with_macro(
+                    self.db,
+                    ResolveMode::Import,
+                    module_id,
+                    &import.path,
+                    BuiltinShadowMode::Module,
+                    None, // An import may resolve to any kind of macro.
+                );
 
-            // Check whether all namespaces are resolved.
-            if def.is_full() {
-                PartialResolvedImport::Resolved(def)
-            } else {
-                PartialResolvedImport::Indeterminate(def)
+                let def = res.resolved_def;
+                if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
+                    return PartialResolvedImport::Unresolved;
+                }
+
+                if let Some(krate) = res.krate {
+                    if krate != self.def_map.krate {
+                        return PartialResolvedImport::Resolved(
+                            def.filter_visibility(|v| matches!(v, Visibility::Public)),
+                        );
+                    }
+                }
+
+                // Check whether all namespaces are resolved.
+                if def.is_full() {
+                    PartialResolvedImport::Resolved(def)
+                } else {
+                    PartialResolvedImport::Indeterminate(def)
+                }
             }
         }
     }
@@ -837,8 +841,9 @@ impl DefCollector<'_> {
             .resolve_visibility(self.db, module_id, &directive.import.visibility, false)
             .unwrap_or(Visibility::Public);
 
-        match import.kind {
-            ImportKind::Plain | ImportKind::TypeOnly => {
+        match import.source {
+            ImportSource::ExternCrate { .. }
+            | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
                 let name = match &import.alias {
                     Some(ImportAlias::Alias(name)) => Some(name),
                     Some(ImportAlias::Underscore) => None,
@@ -851,40 +856,44 @@ impl DefCollector<'_> {
                     },
                 };
 
-                if import.kind == ImportKind::TypeOnly {
-                    def.values = None;
-                    def.macros = None;
-                }
-
+                let imp = match import.source {
+                    // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
+                    ImportSource::ExternCrate { id, .. } => {
+                        if self.def_map.block.is_none() && module_id == DefMap::ROOT {
+                            if let (Some(ModuleDefId::ModuleId(def)), Some(name)) =
+                                (def.take_types(), name)
+                            {
+                                if let Ok(def) = def.try_into() {
+                                    Arc::get_mut(&mut self.def_map.data)
+                                        .unwrap()
+                                        .extern_prelude
+                                        .insert(name.clone(), (def, Some(id)));
+                                }
+                            }
+                        }
+                        ImportType::ExternCrate(id)
+                    }
+                    ImportSource::Use { kind, id, use_tree, .. } => {
+                        if kind == ImportKind::TypeOnly {
+                            def.values = None;
+                            def.macros = None;
+                        }
+                        ImportType::Import(ImportId { import: id, idx: use_tree })
+                    }
+                };
                 tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
 
-                // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
-                if matches!(import.source, ImportSource::ExternCrate { .. })
-                    && self.def_map.block.is_none()
-                    && module_id == DefMap::ROOT
-                {
-                    if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
-                    {
-                        if let Ok(def) = def.try_into() {
-                            Arc::get_mut(&mut self.def_map.data)
-                                .unwrap()
-                                .extern_prelude
-                                .insert(name.clone(), def);
-                        }
-                    }
-                }
-
-                self.update(module_id, &[(name.cloned(), def)], vis, ImportType::Named);
+                self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
             }
-            ImportKind::Glob => {
+            ImportSource::Use { kind: ImportKind::Glob, id, .. } => {
                 tracing::debug!("glob import: {:?}", import);
                 match def.take_types() {
                     Some(ModuleDefId::ModuleId(m)) => {
-                        if import.is_prelude {
+                        if let ImportSource::Use { id, is_prelude: true, .. } = import.source {
                             // Note: This dodgily overrides the injected prelude. The rustc
                             // implementation seems to work the same though.
                             cov_mark::hit!(std_prelude);
-                            self.def_map.prelude = Some(m);
+                            self.def_map.prelude = Some((m, Some(id)));
                         } else if m.krate != self.def_map.krate {
                             cov_mark::hit!(glob_across_crates);
                             // glob import from other crate => we can just import everything once
@@ -901,7 +910,7 @@ impl DefCollector<'_> {
                                 .filter(|(_, res)| !res.is_none())
                                 .collect::<Vec<_>>();
 
-                            self.update(module_id, &items, vis, ImportType::Glob);
+                            self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
                         } else {
                             // glob import from same crate => we do an initial
                             // import, and then need to propagate any further
@@ -933,11 +942,11 @@ impl DefCollector<'_> {
                                 .filter(|(_, res)| !res.is_none())
                                 .collect::<Vec<_>>();
 
-                            self.update(module_id, &items, vis, ImportType::Glob);
+                            self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
                             // record the glob import in case we add further items
                             let glob = self.glob_imports.entry(m.local_id).or_default();
-                            if !glob.iter().any(|(mid, _)| *mid == module_id) {
-                                glob.push((module_id, vis));
+                            if !glob.iter().any(|(mid, _, _)| *mid == module_id) {
+                                glob.push((module_id, vis, id));
                             }
                         }
                     }
@@ -959,11 +968,11 @@ impl DefCollector<'_> {
                             .map(|(local_id, variant_data)| {
                                 let name = variant_data.name.clone();
                                 let variant = EnumVariantId { parent: e, local_id };
-                                let res = PerNs::both(variant.into(), variant.into(), vis);
+                                let res = PerNs::both(variant.into(), variant.into(), vis, None);
                                 (Some(name), res)
                             })
                             .collect::<Vec<_>>();
-                        self.update(module_id, &resolutions, vis, ImportType::Glob);
+                        self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id)));
                     }
                     Some(d) => {
                         tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@@ -983,10 +992,10 @@ impl DefCollector<'_> {
         resolutions: &[(Option<Name>, PerNs)],
         // Visibility this import will have
         vis: Visibility,
-        import_type: ImportType,
+        import: Option<ImportType>,
     ) {
         self.db.unwind_if_cancelled();
-        self.update_recursive(module_id, resolutions, vis, import_type, 0)
+        self.update_recursive(module_id, resolutions, vis, import, 0)
     }
 
     fn update_recursive(
@@ -997,7 +1006,7 @@ impl DefCollector<'_> {
         // All resolutions are imported with this visibility; the visibilities in
         // the `PerNs` values are ignored and overwritten
         vis: Visibility,
-        import_type: ImportType,
+        import: Option<ImportType>,
         depth: usize,
     ) {
         if GLOB_RECURSION_LIMIT.check(depth).is_err() {
@@ -1014,7 +1023,7 @@ impl DefCollector<'_> {
                         &mut self.from_glob_import,
                         (module_id, name.clone()),
                         res.with_visibility(vis),
-                        import_type,
+                        import,
                     );
                 }
                 None => {
@@ -1059,7 +1068,7 @@ impl DefCollector<'_> {
             .get(&module_id)
             .into_iter()
             .flatten()
-            .filter(|(glob_importing_module, _)| {
+            .filter(|(glob_importing_module, _, _)| {
                 // we know all resolutions have the same visibility (`vis`), so we
                 // just need to check that once
                 vis.is_visible_from_def_map(self.db, &self.def_map, *glob_importing_module)
@@ -1067,12 +1076,12 @@ impl DefCollector<'_> {
             .cloned()
             .collect::<Vec<_>>();
 
-        for (glob_importing_module, glob_import_vis) in glob_imports {
+        for (glob_importing_module, glob_import_vis, use_) in glob_imports {
             self.update_recursive(
                 glob_importing_module,
                 resolutions,
                 glob_import_vis,
-                ImportType::Glob,
+                Some(ImportType::Glob(use_)),
                 depth + 1,
             );
         }
@@ -1460,31 +1469,34 @@ impl DefCollector<'_> {
         // heuristic, but it works in practice.
         let mut diagnosed_extern_crates = FxHashSet::default();
         for directive in &self.unresolved_imports {
-            if let ImportSource::ExternCrate(krate) = directive.import.source {
-                let item_tree = krate.item_tree(self.db);
-                let extern_crate = &item_tree[krate.value];
+            if let ImportSource::ExternCrate { id } = directive.import.source {
+                let item_tree_id = id.lookup(self.db).id;
+                let item_tree = item_tree_id.item_tree(self.db);
+                let extern_crate = &item_tree[item_tree_id.value];
 
                 diagnosed_extern_crates.insert(extern_crate.name.clone());
 
                 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
                     directive.module_id,
-                    InFile::new(krate.file_id(), extern_crate.ast_id),
+                    InFile::new(item_tree_id.file_id(), extern_crate.ast_id),
                 ));
             }
         }
 
         for directive in &self.unresolved_imports {
-            if let ImportSource::Use { id: import, use_tree } = directive.import.source {
+            if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } =
+                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;
                 }
-
+                let item_tree_id = id.lookup(self.db).id;
                 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
                     directive.module_id,
-                    import,
+                    item_tree_id,
                     use_tree,
                 ));
             }
@@ -1519,72 +1531,66 @@ impl ModCollector<'_, '_> {
         self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
 
         // Prelude module is always considered to be `#[macro_use]`.
-        if let Some(prelude_module) = self.def_collector.def_map.prelude {
+        if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
             if prelude_module.krate != krate && is_crate_root {
                 cov_mark::hit!(prelude_is_macro_use);
-                self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
+                self.def_collector.import_macros_from_extern_crate(
+                    prelude_module.krate,
+                    None,
+                    None,
+                );
             }
         }
+        let db = self.def_collector.db;
+        let module_id = self.module_id;
+        let update_def =
+            |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
+                def_collector.def_map.modules[module_id].scope.declare(id);
+                def_collector.update(
+                    module_id,
+                    &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor, None))],
+                    vis,
+                    None,
+                )
+            };
+        let resolve_vis = |def_map: &DefMap, visibility| {
+            def_map
+                .resolve_visibility(db, module_id, visibility, false)
+                .unwrap_or(Visibility::Public)
+        };
 
-        // This should be processed eagerly instead of deferred to resolving.
-        // `#[macro_use] extern crate` is hoisted to imports macros before collecting
-        // any other items.
-        //
-        // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
-        // ignore them.
-        if is_crate_root {
-            for &item in items {
-                if let ModItem::ExternCrate(id) = item {
-                    self.process_macro_use_extern_crate(id);
-                }
-            }
-        }
-
-        for &item in items {
-            let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
+        let mut process_mod_item = |item: ModItem| {
+            let attrs = self.item_tree.attrs(db, krate, item.into());
             if let Some(cfg) = attrs.cfg() {
                 if !self.is_cfg_enabled(&cfg) {
                     self.emit_unconfigured_diagnostic(item, &cfg);
-                    continue;
+                    return;
                 }
             }
 
             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;
+                return;
             }
 
-            let db = self.def_collector.db;
-            let module = self.def_collector.def_map.module_id(self.module_id);
+            let module = self.def_collector.def_map.module_id(module_id);
             let def_map = &mut self.def_collector.def_map;
-            let update_def =
-                |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
-                    def_collector.def_map.modules[self.module_id].scope.declare(id);
-                    def_collector.update(
-                        self.module_id,
-                        &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
-                        vis,
-                        ImportType::Named,
-                    )
-                };
-            let resolve_vis = |def_map: &DefMap, visibility| {
-                def_map
-                    .resolve_visibility(db, self.module_id, visibility, false)
-                    .unwrap_or(Visibility::Public)
-            };
 
             match item {
                 ModItem::Mod(m) => self.collect_module(m, &attrs),
-                ModItem::Use(import_id) => {
-                    let _import_id =
-                        UseLoc { container: module, id: ItemTreeId::new(self.tree_id, import_id) }
-                            .intern(db);
+                ModItem::Use(item_tree_id) => {
+                    let id = UseLoc {
+                        container: module,
+                        id: ItemTreeId::new(self.tree_id, item_tree_id),
+                    }
+                    .intern(db);
+                    let is_prelude = attrs.by_key("prelude_import").exists();
                     Import::from_use(
-                        db,
-                        krate,
                         self.item_tree,
-                        ItemTreeId::new(self.tree_id, import_id),
+                        ItemTreeId::new(self.tree_id, item_tree_id),
+                        id,
+                        is_prelude,
                         |import| {
                             self.def_collector.unresolved_imports.push(ImportDirective {
                                 module_id: self.module_id,
@@ -1594,22 +1600,29 @@ impl ModCollector<'_, '_> {
                         },
                     )
                 }
-                ModItem::ExternCrate(import_id) => {
-                    let extern_crate_id = ExternCrateLoc {
+                ModItem::ExternCrate(item_tree_id) => {
+                    let id = ExternCrateLoc {
                         container: module,
-                        id: ItemTreeId::new(self.tree_id, import_id),
+                        id: ItemTreeId::new(self.tree_id, item_tree_id),
                     }
                     .intern(db);
+                    if is_crate_root {
+                        self.process_macro_use_extern_crate(
+                            item_tree_id,
+                            id,
+                            attrs.by_key("macro_use").attrs(),
+                        );
+                    }
+
                     self.def_collector.def_map.modules[self.module_id]
                         .scope
-                        .define_extern_crate_decl(extern_crate_id);
+                        .define_extern_crate_decl(id);
                     self.def_collector.unresolved_imports.push(ImportDirective {
                         module_id: self.module_id,
                         import: Import::from_extern_crate(
-                            db,
-                            krate,
                             self.item_tree,
-                            ItemTreeId::new(self.tree_id, import_id),
+                            ItemTreeId::new(self.tree_id, item_tree_id),
+                            id,
                         ),
                         status: PartialResolvedImport::Unresolved,
                     })
@@ -1768,21 +1781,34 @@ impl ModCollector<'_, '_> {
                     );
                 }
             }
+        };
+
+        // extern crates should be processed eagerly instead of deferred to resolving.
+        // `#[macro_use] extern crate` is hoisted to imports macros before collecting
+        // any other items.
+        if is_crate_root {
+            items
+                .iter()
+                .filter(|it| matches!(it, ModItem::ExternCrate(..)))
+                .copied()
+                .for_each(&mut process_mod_item);
+            items
+                .iter()
+                .filter(|it| !matches!(it, ModItem::ExternCrate(..)))
+                .copied()
+                .for_each(process_mod_item);
+        } else {
+            items.iter().copied().for_each(process_mod_item);
         }
     }
 
-    fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+    fn process_macro_use_extern_crate<'a>(
+        &mut self,
+        extern_crate: FileItemTreeId<ExternCrate>,
+        extern_crate_id: ExternCrateId,
+        macro_use_attrs: impl Iterator<Item = &'a Attr>,
+    ) {
         let db = self.def_collector.db;
-        let attrs = self.item_tree.attrs(
-            db,
-            self.def_collector.def_map.krate,
-            ModItem::from(extern_crate).into(),
-        );
-        if let Some(cfg) = attrs.cfg() {
-            if !self.is_cfg_enabled(&cfg) {
-                return;
-            }
-        }
 
         let target_crate =
             match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
@@ -1798,11 +1824,11 @@ impl ModCollector<'_, '_> {
 
         let mut single_imports = Vec::new();
         let hygiene = Hygiene::new_unhygienic();
-        for attr in attrs.by_key("macro_use").attrs() {
+        for attr in macro_use_attrs {
             let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
                 // `#[macro_use]` (without any paths) found, forget collected names and just import
                 // all visible macros.
-                self.def_collector.import_macros_from_extern_crate(target_crate, None);
+                self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id));
                 return;
             };
             for path in paths {
@@ -1812,7 +1838,11 @@ impl ModCollector<'_, '_> {
             }
         }
 
-        self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+        self.def_collector.import_macros_from_extern_crate(
+            target_crate,
+            Some(single_imports),
+            Some(extern_crate_id),
+        );
     }
 
     fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
@@ -1824,7 +1854,7 @@ impl ModCollector<'_, '_> {
             ModKind::Inline { items } => {
                 let module_id = self.push_child_module(
                     module.name.clone(),
-                    AstId::new(self.file_id(), module.ast_id),
+                    module.ast_id,
                     None,
                     &self.item_tree[module.visibility],
                     module_id,
@@ -1862,7 +1892,7 @@ impl ModCollector<'_, '_> {
                         if is_enabled {
                             let module_id = self.push_child_module(
                                 module.name.clone(),
-                                ast_id,
+                                ast_id.value,
                                 Some((file_id, is_mod_rs)),
                                 &self.item_tree[module.visibility],
                                 module_id,
@@ -1889,7 +1919,7 @@ impl ModCollector<'_, '_> {
                     Err(candidates) => {
                         self.push_child_module(
                             module.name.clone(),
-                            ast_id,
+                            ast_id.value,
                             None,
                             &self.item_tree[module.visibility],
                             module_id,
@@ -1906,7 +1936,7 @@ impl ModCollector<'_, '_> {
     fn push_child_module(
         &mut self,
         name: Name,
-        declaration: AstId<ast::Module>,
+        declaration: FileAstId<ast::Module>,
         definition: Option<(FileId, bool)>,
         visibility: &crate::visibility::RawVisibility,
         mod_tree_id: FileItemTreeId<Mod>,
@@ -1948,9 +1978,9 @@ impl ModCollector<'_, '_> {
         def_map.modules[self.module_id].scope.declare(def);
         self.def_collector.update(
             self.module_id,
-            &[(Some(name), PerNs::from_def(def, vis, false))],
+            &[(Some(name), PerNs::from_def(def, vis, false, None))],
             vis,
-            ImportType::Named,
+            None,
         );
         res
     }
@@ -2198,7 +2228,7 @@ impl ModCollector<'_, '_> {
                             map[module].scope.get_legacy_macro(name)?.last().copied()
                         })
                         .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
-                        .or_else(|| def_map.macro_use_prelude.get(name).copied())
+                        .or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0))
                         .filter(|&id| {
                             sub_namespace_match(
                                 Some(MacroSubNs::from_id(db, id)),
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index de22ea10146..460a908b6db 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -15,8 +15,9 @@ use hir_expand::name::Name;
 use triomphe::Arc;
 
 use crate::{
+    data::adt::VariantData,
     db::DefDatabase,
-    item_scope::BUILTIN_SCOPE,
+    item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
     nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
     path::{ModPath, PathKind},
     per_ns::PerNs,
@@ -65,7 +66,7 @@ impl PerNs {
         db: &dyn DefDatabase,
         expected: Option<MacroSubNs>,
     ) -> Self {
-        self.macros = self.macros.filter(|&(id, _)| {
+        self.macros = self.macros.filter(|&(id, _, _)| {
             let this = MacroSubNs::from_id(db, id);
             sub_namespace_match(Some(this), expected)
         });
@@ -196,15 +197,15 @@ impl DefMap {
             PathKind::DollarCrate(krate) => {
                 if krate == self.krate {
                     cov_mark::hit!(macro_dollar_crate_self);
-                    PerNs::types(self.crate_root().into(), Visibility::Public)
+                    PerNs::types(self.crate_root().into(), Visibility::Public, None)
                 } else {
                     let def_map = db.crate_def_map(krate);
                     let module = def_map.module_id(Self::ROOT);
                     cov_mark::hit!(macro_dollar_crate_other);
-                    PerNs::types(module.into(), Visibility::Public)
+                    PerNs::types(module.into(), Visibility::Public, None)
                 }
             }
-            PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
+            PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public, None),
             // plain import or absolute path in 2015: crate-relative with
             // fallback to extern prelude (with the simplification in
             // rust-lang/rust#57745)
@@ -291,7 +292,7 @@ impl DefMap {
                     );
                 }
 
-                PerNs::types(module.into(), Visibility::Public)
+                PerNs::types(module.into(), Visibility::Public, None)
             }
             PathKind::Abs => {
                 // 2018-style absolute path -- only extern prelude
@@ -299,9 +300,13 @@ impl DefMap {
                     Some((_, segment)) => segment,
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
-                if let Some(&def) = self.data.extern_prelude.get(segment) {
+                if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
                     tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
-                    PerNs::types(def.into(), Visibility::Public)
+                    PerNs::types(
+                        def.into(),
+                        Visibility::Public,
+                        extern_crate.map(ImportOrExternCrate::ExternCrate),
+                    )
                 } else {
                     return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
                 }
@@ -309,7 +314,7 @@ impl DefMap {
         };
 
         for (i, segment) in segments {
-            let (curr, vis) = match curr_per_ns.take_types_vis() {
+            let (curr, vis, imp) = match curr_per_ns.take_types_full() {
                 Some(r) => r,
                 None => {
                     // we still have path segments left, but the path so far
@@ -364,18 +369,20 @@ impl DefMap {
                         Some(local_id) => {
                             let variant = EnumVariantId { parent: e, local_id };
                             match &*enum_data.variants[local_id].variant_data {
-                                crate::data::adt::VariantData::Record(_) => {
-                                    PerNs::types(variant.into(), Visibility::Public)
-                                }
-                                crate::data::adt::VariantData::Tuple(_)
-                                | crate::data::adt::VariantData::Unit => {
-                                    PerNs::both(variant.into(), variant.into(), Visibility::Public)
+                                VariantData::Record(_) => {
+                                    PerNs::types(variant.into(), Visibility::Public, None)
                                 }
+                                VariantData::Tuple(_) | VariantData::Unit => PerNs::both(
+                                    variant.into(),
+                                    variant.into(),
+                                    Visibility::Public,
+                                    None,
+                                ),
                             }
                         }
                         None => {
                             return ResolvePathResult::with(
-                                PerNs::types(e.into(), vis),
+                                PerNs::types(e.into(), vis, imp),
                                 ReachedFixedPoint::Yes,
                                 Some(i),
                                 Some(self.krate),
@@ -393,7 +400,7 @@ impl DefMap {
                     );
 
                     return ResolvePathResult::with(
-                        PerNs::types(s, vis),
+                        PerNs::types(s, vis, imp),
                         ReachedFixedPoint::Yes,
                         Some(i),
                         Some(self.krate),
@@ -430,7 +437,7 @@ impl DefMap {
             .filter(|&id| {
                 sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
             })
-            .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
+            .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public, None));
         let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
         let from_builtin = match self.block {
             Some(_) => {
@@ -449,18 +456,27 @@ impl DefMap {
 
         let extern_prelude = || {
             if self.block.is_some() {
-                // Don't resolve extern prelude in block `DefMap`s.
+                // Don't resolve extern prelude in block `DefMap`s, defer it to the crate def map so
+                // that blocks can properly shadow them
                 return PerNs::none();
             }
-            self.data
-                .extern_prelude
-                .get(name)
-                .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
+            self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
+                PerNs::types(
+                    it.into(),
+                    Visibility::Public,
+                    extern_crate.map(ImportOrExternCrate::ExternCrate),
+                )
+            })
         };
         let macro_use_prelude = || {
-            self.macro_use_prelude
-                .get(name)
-                .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+            self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+                PerNs::macros(
+                    it.into(),
+                    Visibility::Public,
+                    // FIXME?
+                    None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
+                )
+            })
         };
         let prelude = || self.resolve_in_prelude(db, name);
 
@@ -488,18 +504,23 @@ impl DefMap {
                 // Don't resolve extern prelude in block `DefMap`s.
                 return PerNs::none();
             }
-            self.data
-                .extern_prelude
-                .get(name)
-                .copied()
-                .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
+            self.data.extern_prelude.get(name).copied().map_or(
+                PerNs::none(),
+                |(it, extern_crate)| {
+                    PerNs::types(
+                        it.into(),
+                        Visibility::Public,
+                        extern_crate.map(ImportOrExternCrate::ExternCrate),
+                    )
+                },
+            )
         };
 
         from_crate_root.or_else(from_extern_prelude)
     }
 
     fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
-        if let Some(prelude) = self.prelude {
+        if let Some((prelude, _use)) = self.prelude {
             let keep;
             let def_map = if prelude.krate == self.krate {
                 self
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index dd7c3c36306..e7cc44b04da 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -168,7 +168,7 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Foo: t v
+            Foo: ti vi
             foo: t
 
             crate::foo
@@ -194,8 +194,8 @@ pub enum Quux {};
 "#,
         expect![[r#"
             crate
-            Baz: t v
-            Quux: t
+            Baz: ti vi
+            Quux: ti
             foo: t
 
             crate::foo
@@ -225,11 +225,11 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Baz: t v
+            Baz: ti vi
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             bar: t
 
             crate::foo::bar
@@ -274,7 +274,7 @@ use self::E::V;
         expect![[r#"
             crate
             E: t
-            V: t v
+            V: ti vi
         "#]],
     );
 }
@@ -307,7 +307,7 @@ pub struct FromLib;
 
             crate::foo
             Bar: _
-            FromLib: t v
+            FromLib: ti vi
         "#]],
     );
 }
@@ -328,7 +328,7 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Baz: t
+            Baz: ti
             foo: t
 
             crate::foo
@@ -352,7 +352,7 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Baz: t v
+            Baz: ti vi
         "#]],
     );
 }
@@ -375,13 +375,13 @@ pub struct Arc;
         expect![[r#"
             crate
             alloc: t
-            alloc_crate: t
+            alloc_crate: te
             sync: t
 
             crate::alloc
 
             crate::sync
-            Arc: t v
+            Arc: ti vi
         "#]],
     );
 }
@@ -404,13 +404,13 @@ pub struct Arc;
         expect![[r#"
             crate
             alloc: t
-            alloc_crate: t
+            alloc_crate: te
             sync: t
 
             crate::alloc
 
             crate::sync
-            Arc: t v
+            Arc: ti vi
         "#]],
     );
 }
@@ -426,7 +426,7 @@ extern crate self as bla;
 "#,
         expect![[r#"
             crate
-            bla: t
+            bla: te
         "#]],
     );
 }
@@ -447,7 +447,7 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Baz: t v
+            Baz: ti vi
         "#]],
     );
 }
@@ -465,7 +465,7 @@ pub struct Bar;
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             foo: v
         "#]],
     );
@@ -492,9 +492,9 @@ fn no_std_prelude() {
         }
     "#,
         expect![[r#"
-        crate
-        Rust: t v
-    "#]],
+            crate
+            Rust: ti vi
+        "#]],
     );
 }
 
@@ -516,9 +516,9 @@ fn edition_specific_preludes() {
         }
     "#,
         expect![[r#"
-        crate
-        Rust2018: t v
-    "#]],
+            crate
+            Rust2018: ti vi
+        "#]],
     );
     check(
         r#"
@@ -533,9 +533,9 @@ fn edition_specific_preludes() {
         }
     "#,
         expect![[r#"
-        crate
-        Rust2021: t v
-    "#]],
+            crate
+            Rust2021: ti vi
+        "#]],
     );
 }
 
@@ -563,8 +563,8 @@ pub mod prelude {
 "#,
         expect![[r#"
             crate
-            Bar: t v
-            Foo: t v
+            Bar: ti vi
+            Foo: ti vi
         "#]],
     );
 }
@@ -590,7 +590,7 @@ pub mod prelude {
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             Baz: _
             Foo: _
         "#]],
@@ -619,8 +619,8 @@ pub mod prelude {
         expect![[r#"
             crate
             Bar: _
-            Baz: t v
-            Foo: t v
+            Baz: ti vi
+            Foo: ti vi
         "#]],
     );
 }
@@ -643,7 +643,7 @@ mod b {
 "#,
         expect![[r#"
             crate
-            T: t v
+            T: ti vi
             a: t
             b: t
 
@@ -816,8 +816,8 @@ fn bar() {}
         expect![[r#"
             crate
             bar: v
-            baz: v
-            foo: t
+            baz: vi
+            foo: ti
         "#]],
     );
 }
@@ -836,7 +836,7 @@ use self::m::S::{self};
     "#,
         expect![[r#"
             crate
-            S: t
+            S: ti
             m: t
 
             crate::m
@@ -860,8 +860,8 @@ pub const settings: () = ();
         "#,
         expect![[r#"
             crate
-            Settings: t v
-            settings: v
+            Settings: ti vi
+            settings: vi
         "#]],
     )
 }
@@ -890,8 +890,8 @@ pub struct Struct;
         "#,
         expect![[r#"
             crate
-            Struct: t v
-            dep: t
+            Struct: ti vi
+            dep: te
         "#]],
     );
 }
@@ -917,13 +917,13 @@ use some_module::unknown_func;
             crate
             other_module: t
             some_module: t
-            unknown_func: v
+            unknown_func: vi
 
             crate::other_module
             some_submodule: t
 
             crate::other_module::some_submodule
-            unknown_func: v
+            unknown_func: vi
 
             crate::some_module
             unknown_func: v
diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs
index 88a3c76393f..1ca74b5da6b 100644
--- a/crates/hir-def/src/nameres/tests/globs.rs
+++ b/crates/hir-def/src/nameres/tests/globs.rs
@@ -24,7 +24,7 @@ pub struct Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             Foo: t v
             bar: t
 
@@ -237,9 +237,9 @@ pub mod baz { pub struct Bar; }
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             bar: t
-            baz: t
+            baz: ti
             foo: t
 
             crate::bar
@@ -276,9 +276,9 @@ pub mod baz { pub struct Bar; }
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             bar: t
-            baz: t
+            baz: ti
             foo: t
 
             crate::bar
@@ -323,7 +323,7 @@ mod d {
             X: t v
 
             crate::b
-            foo: t
+            foo: ti
 
             crate::c
             foo: t
@@ -332,8 +332,8 @@ mod d {
             Y: t v
 
             crate::d
-            Y: t v
-            foo: t
+            Y: ti vi
+            foo: ti
         "#]],
     );
 }
@@ -355,7 +355,7 @@ use event::Event;
         "#,
         expect![[r#"
             crate
-            Event: t
+            Event: ti
             event: t
 
             crate::event
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 40d3a16540d..4a86f88e57a 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -212,7 +212,7 @@ pub type Ty = ();
             }
 
             for (_, res) in module_data.scope.resolutions() {
-                match res.values.or(res.types).unwrap().0 {
+                match res.values.map(|(a, _, _)| a).or(res.types.map(|(a, _, _)| a)).unwrap() {
                     ModuleDefId::FunctionId(f) => _ = db.function_data(f),
                     ModuleDefId::AdtId(adt) => match adt {
                         AdtId::StructId(it) => _ = db.struct_data(it),
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index f4cca8d68d0..e64fa0b46f1 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -203,8 +203,8 @@ macro_rules! bar {
         expect![[r#"
             crate
             Foo: t
-            bar: m
-            foo: m
+            bar: mi
+            foo: mi
         "#]],
     );
 }
@@ -251,7 +251,7 @@ mod priv_mod {
             Bar: t v
             Foo: t v
             bar: t
-            foo: t
+            foo: te
 
             crate::bar
             Baz: t v
@@ -318,9 +318,9 @@ macro_rules! baz3 { () => { struct OkBaz3; } }
             OkBaz1: t v
             OkBaz2: t v
             OkBaz3: t v
-            all: t
-            empty: t
-            multiple: t
+            all: te
+            empty: te
+            multiple: te
         "#]],
     );
 }
@@ -551,8 +551,8 @@ fn baz() {}
 "#,
         expect![[r#"
             crate
-            bar: t m
-            baz: t v m
+            bar: ti mi
+            baz: ti v mi
             foo: t m
         "#]],
     );
@@ -583,7 +583,7 @@ mod m {
             crate
             Alias: t v
             Direct: t v
-            foo: t
+            foo: te
         "#]],
     );
 }
@@ -628,9 +628,9 @@ mod m {
             m: t
 
             crate::m
-            alias1: m
-            alias2: m
-            alias3: m
+            alias1: mi
+            alias2: mi
+            alias3: mi
             not_found: _
         "#]],
     );
@@ -682,11 +682,11 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Bar: t v
-            Baz: t v
+            Bar: ti vi
+            Baz: ti vi
             Foo: t v
-            FooSelf: t v
-            foo: t
+            FooSelf: ti vi
+            foo: te
             m: t
 
             crate::m
@@ -725,7 +725,7 @@ pub struct bar;
 "#,
         expect![[r#"
             crate
-            bar: t v
+            bar: ti vi
         "#]],
     );
 }
@@ -1340,7 +1340,7 @@ pub mod prelude {
             crate
             Ok: t v
             bar: m
-            dep: t
+            dep: te
             foo: m
             ok: v
         "#]],
@@ -1370,13 +1370,13 @@ macro_rules! mk_foo {
 }
     "#,
         expect![[r#"
-        crate
-        a: t
-        lib: t
+            crate
+            a: t
+            lib: te
 
-        crate::a
-        Ok: t v
-    "#]],
+            crate::a
+            Ok: t v
+        "#]],
     );
 }
 
@@ -1427,8 +1427,8 @@ pub mod prelude {
         expect![[r#"
             crate
             Ok: t v
-            bar: m
-            foo: m
+            bar: mi
+            foo: mi
             ok: v
         "#]],
     );
diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs
index 81bc0ff91e3..1327d9aa62e 100644
--- a/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -80,18 +80,18 @@ pub trait Iterator;
             prelude: t
 
             crate::iter
-            Iterator: t
+            Iterator: ti
             traits: t
 
             crate::iter::traits
-            Iterator: t
+            Iterator: ti
             iterator: t
 
             crate::iter::traits::iterator
             Iterator: t
 
             crate::prelude
-            Iterator: t
+            Iterator: ti
         "#]],
     );
 }
@@ -109,7 +109,7 @@ pub struct Bar;
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             foo: t
 
             crate::foo
@@ -139,7 +139,7 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             r#async: t
 
             crate::r#async
@@ -176,8 +176,8 @@ pub struct Bar;
 "#,
         expect![[r#"
             crate
-            Bar: t v
-            Foo: t v
+            Bar: ti vi
+            Foo: ti vi
             r#async: t
 
             crate::r#async
@@ -207,7 +207,7 @@ pub struct Bar;
 "#,
         expect![[r#"
             crate
-            Bar: t v
+            Bar: ti vi
             foo: t
 
             crate::foo
@@ -236,7 +236,7 @@ pub struct Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             bar: t
 
             crate::foo::bar
@@ -265,7 +265,7 @@ pub struct Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             bar: t
 
             crate::foo::bar
@@ -292,7 +292,7 @@ use super::Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
         "#]],
     );
 }
@@ -626,7 +626,7 @@ pub struct Baz;
 "#,
         expect![[r#"
             crate
-            Baz: t v
+            Baz: ti vi
             foo: t
 
             crate::foo
@@ -660,7 +660,7 @@ pub struct Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             bar: t
 
             crate::foo::bar
@@ -694,7 +694,7 @@ pub struct Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             bar: t
 
             crate::foo::bar
@@ -728,7 +728,7 @@ pub struct Baz;
             foo: t
 
             crate::foo
-            Baz: t v
+            Baz: ti vi
             bar: t
 
             crate::foo::bar
@@ -868,7 +868,7 @@ pub mod hash { pub trait Hash {} }
 "#,
         expect![[r#"
             crate
-            Hash: t
+            Hash: ti
             core: t
 
             crate::core
diff --git a/crates/hir-def/src/nameres/tests/primitives.rs b/crates/hir-def/src/nameres/tests/primitives.rs
index 215e8952d90..271eb1c79b1 100644
--- a/crates/hir-def/src/nameres/tests/primitives.rs
+++ b/crates/hir-def/src/nameres/tests/primitives.rs
@@ -14,10 +14,10 @@ pub use i32 as int;
         expect![[r#"
             crate
             foo: t
-            int: t
+            int: ti
 
             crate::foo
-            int: t
+            int: ti
         "#]],
     );
 }
diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs
index 2bc1f8e926e..14890364d0b 100644
--- a/crates/hir-def/src/per_ns.rs
+++ b/crates/hir-def/src/per_ns.rs
@@ -3,13 +3,24 @@
 //!
 //! `PerNs` (per namespace) captures this.
 
-use crate::{item_scope::ItemInNs, visibility::Visibility, MacroId, ModuleDefId};
+use crate::{
+    item_scope::{ImportId, ImportOrExternCrate, ItemInNs},
+    visibility::Visibility,
+    MacroId, ModuleDefId,
+};
+
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
+pub enum Namespace {
+    Types,
+    Values,
+    Macros,
+}
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct PerNs {
-    pub types: Option<(ModuleDefId, Visibility)>,
-    pub values: Option<(ModuleDefId, Visibility)>,
-    pub macros: Option<(MacroId, Visibility)>,
+    pub types: Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
+    pub values: Option<(ModuleDefId, Visibility, Option<ImportId>)>,
+    pub macros: Option<(MacroId, Visibility, Option<ImportId>)>,
 }
 
 impl Default for PerNs {
@@ -23,20 +34,29 @@ impl PerNs {
         PerNs { types: None, values: None, macros: None }
     }
 
-    pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
-        PerNs { types: None, values: Some((t, v)), macros: None }
+    pub fn values(t: ModuleDefId, v: Visibility, i: Option<ImportId>) -> PerNs {
+        PerNs { types: None, values: Some((t, v, i)), macros: None }
     }
 
-    pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
-        PerNs { types: Some((t, v)), values: None, macros: None }
+    pub fn types(t: ModuleDefId, v: Visibility, i: Option<ImportOrExternCrate>) -> PerNs {
+        PerNs { types: Some((t, v, i)), values: None, macros: None }
     }
 
-    pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
-        PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
+    pub fn both(
+        types: ModuleDefId,
+        values: ModuleDefId,
+        v: Visibility,
+        i: Option<ImportOrExternCrate>,
+    ) -> PerNs {
+        PerNs {
+            types: Some((types, v, i)),
+            values: Some((values, v, i.and_then(ImportOrExternCrate::into_import))),
+            macros: None,
+        }
     }
 
-    pub fn macros(macro_: MacroId, v: Visibility) -> PerNs {
-        PerNs { types: None, values: None, macros: Some((macro_, v)) }
+    pub fn macros(macro_: MacroId, v: Visibility, i: Option<ImportId>) -> PerNs {
+        PerNs { types: None, values: None, macros: Some((macro_, v, i)) }
     }
 
     pub fn is_none(&self) -> bool {
@@ -51,7 +71,7 @@ impl PerNs {
         self.types.map(|it| it.0)
     }
 
-    pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
+    pub fn take_types_full(self) -> Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)> {
         self.types
     }
 
@@ -59,24 +79,32 @@ impl PerNs {
         self.values.map(|it| it.0)
     }
 
+    pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> {
+        self.values.map(|it| (it.0, it.2))
+    }
+
     pub fn take_macros(self) -> Option<MacroId> {
         self.macros.map(|it| it.0)
     }
 
+    pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> {
+        self.macros.map(|it| (it.0, it.2))
+    }
+
     pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
         let _p = profile::span("PerNs::filter_visibility");
         PerNs {
-            types: self.types.filter(|(_, v)| f(*v)),
-            values: self.values.filter(|(_, v)| f(*v)),
-            macros: self.macros.filter(|(_, v)| f(*v)),
+            types: self.types.filter(|&(_, v, _)| f(v)),
+            values: self.values.filter(|&(_, v, _)| f(v)),
+            macros: self.macros.filter(|&(_, v, _)| f(v)),
         }
     }
 
     pub fn with_visibility(self, vis: Visibility) -> PerNs {
         PerNs {
-            types: self.types.map(|(it, _)| (it, vis)),
-            values: self.values.map(|(it, _)| (it, vis)),
-            macros: self.macros.map(|(it, _)| (it, vis)),
+            types: self.types.map(|(it, _, c)| (it, vis, c)),
+            values: self.values.map(|(it, _, c)| (it, vis, c)),
+            macros: self.macros.map(|(it, _, import)| (it, vis, import)),
         }
     }
 
@@ -96,12 +124,20 @@ impl PerNs {
         }
     }
 
-    pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
+    pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> {
         let _p = profile::span("PerNs::iter_items");
         self.types
-            .map(|it| ItemInNs::Types(it.0))
+            .map(|it| (ItemInNs::Types(it.0), it.2))
             .into_iter()
-            .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
-            .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
+            .chain(
+                self.values
+                    .map(|it| (ItemInNs::Values(it.0), it.2.map(ImportOrExternCrate::Import)))
+                    .into_iter(),
+            )
+            .chain(
+                self.macros
+                    .map(|it| (ItemInNs::Macros(it.0), it.2.map(ImportOrExternCrate::Import)))
+                    .into_iter(),
+            )
     }
 }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index b112c1070d4..2f9187009e2 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -12,20 +12,21 @@ use triomphe::Arc;
 use crate::{
     body::scope::{ExprScopes, ScopeId},
     builtin_type::BuiltinType,
+    data::ExternCrateDeclData,
     db::DefDatabase,
     generics::{GenericParams, TypeOrConstParamData},
     hir::{BindingId, ExprId, LabelId},
-    item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
+    item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
     lang_item::LangItemTarget,
     nameres::{DefMap, MacroSubNs},
     path::{ModPath, Path, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
-    AdtId, AssocItemId, 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,
+    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,
 };
 
 #[derive(Debug, Clone)]
@@ -100,8 +101,8 @@ pub enum TypeNs {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum ResolveValueResult {
-    ValueNs(ValueNs),
-    Partial(TypeNs, usize),
+    ValueNs(ValueNs, Option<ImportId>),
+    Partial(TypeNs, usize, Option<ImportOrExternCrate>),
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -148,39 +149,11 @@ impl Resolver {
         self.resolve_module_path(db, path, BuiltinShadowMode::Module)
     }
 
-    // FIXME: This shouldn't exist
-    pub fn resolve_module_path_in_trait_assoc_items(
-        &self,
-        db: &dyn DefDatabase,
-        path: &ModPath,
-    ) -> Option<PerNs> {
-        let (item_map, module) = self.item_scope();
-        let (module_res, idx) =
-            item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
-        match module_res.take_types()? {
-            ModuleDefId::TraitId(it) => {
-                let idx = idx?;
-                let unresolved = &path.segments()[idx..];
-                let assoc = match unresolved {
-                    [it] => it,
-                    _ => return None,
-                };
-                let &(_, assoc) = db.trait_data(it).items.iter().find(|(n, _)| n == assoc)?;
-                Some(match assoc {
-                    AssocItemId::FunctionId(it) => PerNs::values(it.into(), Visibility::Public),
-                    AssocItemId::ConstId(it) => PerNs::values(it.into(), Visibility::Public),
-                    AssocItemId::TypeAliasId(it) => PerNs::types(it.into(), Visibility::Public),
-                })
-            }
-            _ => None,
-        }
-    }
-
     pub fn resolve_path_in_type_ns(
         &self,
         db: &dyn DefDatabase,
         path: &Path,
-    ) -> Option<(TypeNs, Option<usize>)> {
+    ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
         let path = match path {
             Path::Normal { mod_path, .. } => mod_path,
             Path::LangItem(l) => {
@@ -197,6 +170,7 @@ impl Resolver {
                         | LangItemTarget::Static(_) => return None,
                     },
                     None,
+                    None,
                 ))
             }
         };
@@ -213,17 +187,17 @@ impl Resolver {
                 Scope::ExprScope(_) => continue,
                 Scope::GenericParams { params, def } => {
                     if let Some(id) = params.find_type_by_name(first_name, *def) {
-                        return Some((TypeNs::GenericParam(id), remaining_idx()));
+                        return Some((TypeNs::GenericParam(id), remaining_idx(), None));
                     }
                 }
                 &Scope::ImplDefScope(impl_) => {
                     if first_name == &name![Self] {
-                        return Some((TypeNs::SelfType(impl_), remaining_idx()));
+                        return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
                     }
                 }
                 &Scope::AdtScope(adt) => {
                     if first_name == &name![Self] {
-                        return Some((TypeNs::AdtSelfType(adt), remaining_idx()));
+                        return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
                     }
                 }
                 Scope::BlockScope(m) => {
@@ -236,12 +210,24 @@ impl Resolver {
         self.module_scope.resolve_path_in_type_ns(db, path)
     }
 
+    pub fn resolve_path_in_type_ns_fully_with_imports(
+        &self,
+        db: &dyn DefDatabase,
+        path: &Path,
+    ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+        let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?;
+        if unresolved.is_some() {
+            return None;
+        }
+        Some((res, imp))
+    }
+
     pub fn resolve_path_in_type_ns_fully(
         &self,
         db: &dyn DefDatabase,
         path: &Path,
     ) -> Option<TypeNs> {
-        let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
+        let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?;
         if unresolved.is_some() {
             return None;
         }
@@ -263,7 +249,6 @@ impl Resolver {
             RawVisibility::Public => Some(Visibility::Public),
         }
     }
-
     pub fn resolve_path_in_value_ns(
         &self,
         db: &dyn DefDatabase,
@@ -272,17 +257,20 @@ impl Resolver {
         let path = match path {
             Path::Normal { mod_path, .. } => mod_path,
             Path::LangItem(l) => {
-                return Some(ResolveValueResult::ValueNs(match *l {
-                    LangItemTarget::Function(it) => ValueNs::FunctionId(it),
-                    LangItemTarget::Static(it) => ValueNs::StaticId(it),
-                    LangItemTarget::Struct(it) => ValueNs::StructId(it),
-                    LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
-                    LangItemTarget::Union(_)
-                    | LangItemTarget::ImplDef(_)
-                    | LangItemTarget::TypeAlias(_)
-                    | LangItemTarget::Trait(_)
-                    | LangItemTarget::EnumId(_) => return None,
-                }))
+                return Some(ResolveValueResult::ValueNs(
+                    match *l {
+                        LangItemTarget::Function(it) => ValueNs::FunctionId(it),
+                        LangItemTarget::Static(it) => ValueNs::StaticId(it),
+                        LangItemTarget::Struct(it) => ValueNs::StructId(it),
+                        LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
+                        LangItemTarget::Union(_)
+                        | LangItemTarget::ImplDef(_)
+                        | LangItemTarget::TypeAlias(_)
+                        | LangItemTarget::Trait(_)
+                        | LangItemTarget::EnumId(_) => return None,
+                    },
+                    None,
+                ))
             }
         };
         let n_segments = path.segments().len();
@@ -304,20 +292,24 @@ impl Resolver {
                             .find(|entry| entry.name() == first_name);
 
                         if let Some(e) = entry {
-                            return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(
-                                e.binding(),
-                            )));
+                            return Some(ResolveValueResult::ValueNs(
+                                ValueNs::LocalBinding(e.binding()),
+                                None,
+                            ));
                         }
                     }
                     Scope::GenericParams { params, def } => {
                         if let Some(id) = params.find_const_by_name(first_name, *def) {
                             let val = ValueNs::GenericParam(id);
-                            return Some(ResolveValueResult::ValueNs(val));
+                            return Some(ResolveValueResult::ValueNs(val, None));
                         }
                     }
                     &Scope::ImplDefScope(impl_) => {
                         if first_name == &name![Self] {
-                            return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)));
+                            return Some(ResolveValueResult::ValueNs(
+                                ValueNs::ImplSelf(impl_),
+                                None,
+                            ));
                         }
                     }
                     // bare `Self` doesn't work in the value namespace in a struct/enum definition
@@ -336,18 +328,22 @@ impl Resolver {
                     Scope::GenericParams { params, def } => {
                         if let Some(id) = params.find_type_by_name(first_name, *def) {
                             let ty = TypeNs::GenericParam(id);
-                            return Some(ResolveValueResult::Partial(ty, 1));
+                            return Some(ResolveValueResult::Partial(ty, 1, None));
                         }
                     }
                     &Scope::ImplDefScope(impl_) => {
                         if first_name == &name![Self] {
-                            return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1));
+                            return Some(ResolveValueResult::Partial(
+                                TypeNs::SelfType(impl_),
+                                1,
+                                None,
+                            ));
                         }
                     }
                     Scope::AdtScope(adt) => {
                         if first_name == &name![Self] {
                             let ty = TypeNs::AdtSelfType(*adt);
-                            return Some(ResolveValueResult::Partial(ty, 1));
+                            return Some(ResolveValueResult::Partial(ty, 1, None));
                         }
                     }
                     Scope::BlockScope(m) => {
@@ -368,7 +364,7 @@ impl Resolver {
         // `use core::u16;`.
         if path.kind == PathKind::Plain && n_segments > 1 {
             if let Some(builtin) = BuiltinType::by_name(first_name) {
-                return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1));
+                return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None));
             }
         }
 
@@ -381,7 +377,7 @@ impl Resolver {
         path: &Path,
     ) -> Option<ValueNs> {
         match self.resolve_path_in_value_ns(db, path)? {
-            ResolveValueResult::ValueNs(it) => Some(it),
+            ResolveValueResult::ValueNs(it, _) => Some(it),
             ResolveValueResult::Partial(..) => None,
         }
     }
@@ -391,12 +387,12 @@ impl Resolver {
         db: &dyn DefDatabase,
         path: &ModPath,
         expected_macro_kind: Option<MacroSubNs>,
-    ) -> Option<MacroId> {
+    ) -> Option<(MacroId, Option<ImportId>)> {
         let (item_map, module) = self.item_scope();
         item_map
             .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
             .0
-            .take_macros()
+            .take_macros_import()
     }
 
     /// Returns a set of names available in the current scope.
@@ -456,21 +452,22 @@ impl Resolver {
         def_map[module_id].scope.entries().for_each(|(name, def)| {
             res.add_per_ns(name, def);
         });
+
         def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| {
             macs.iter().for_each(|&mac| {
                 res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
             })
         });
-        def_map.macro_use_prelude().for_each(|(name, def)| {
+        def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| {
             res.add(name, ScopeDef::ModuleDef(def.into()));
         });
-        def_map.extern_prelude().for_each(|(name, def)| {
-            res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
+        def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
+            res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
         });
         BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
             res.add_per_ns(name, def);
         });
-        if let Some(prelude) = def_map.prelude() {
+        if let Some((prelude, _use)) = def_map.prelude() {
             let prelude_def_map = prelude.def_map(db);
             for (name, def) in prelude_def_map[prelude.local_id].scope.entries() {
                 res.add_per_ns(name, def)
@@ -479,6 +476,23 @@ impl Resolver {
         res.map
     }
 
+    pub fn extern_crate_decls_in_scope<'a>(
+        &'a self,
+        db: &'a dyn DefDatabase,
+    ) -> impl Iterator<Item = Name> + 'a {
+        self.module_scope.def_map[self.module_scope.module_id]
+            .scope
+            .extern_crate_decls()
+            .map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone())
+    }
+
+    pub fn extern_crates_in_scope<'a>(&'a self) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
+        self.module_scope
+            .def_map
+            .extern_prelude()
+            .map(|(name, module_id)| (name.clone(), module_id.0.into()))
+    }
+
     pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
         // FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
         // aliased traits are NOT brought in scope (unless also aliased).
@@ -501,7 +515,7 @@ impl Resolver {
         }
 
         // Fill in the prelude traits
-        if let Some(prelude) = self.module_scope.def_map.prelude() {
+        if let Some((prelude, _use)) = self.module_scope.def_map.prelude() {
             let prelude_def_map = prelude.def_map(db);
             traits.extend(prelude_def_map[prelude.local_id].scope.traits());
         }
@@ -804,11 +818,12 @@ impl ModuleItemMap {
             self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
         match idx {
             None => {
-                let value = to_value_ns(module_def)?;
-                Some(ResolveValueResult::ValueNs(value))
+                let (value, import) = to_value_ns(module_def)?;
+                Some(ResolveValueResult::ValueNs(value, import))
             }
             Some(idx) => {
-                let ty = match module_def.take_types()? {
+                let (def, _, import) = module_def.take_types_full()?;
+                let ty = match def {
                     ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
                     ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
                     ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
@@ -822,7 +837,7 @@ impl ModuleItemMap {
                     | ModuleDefId::MacroId(_)
                     | ModuleDefId::StaticId(_) => return None,
                 };
-                Some(ResolveValueResult::Partial(ty, idx))
+                Some(ResolveValueResult::Partial(ty, idx, import))
             }
         }
     }
@@ -831,16 +846,17 @@ impl ModuleItemMap {
         &self,
         db: &dyn DefDatabase,
         path: &ModPath,
-    ) -> Option<(TypeNs, Option<usize>)> {
+    ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
         let (module_def, idx) =
             self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
-        let res = to_type_ns(module_def)?;
-        Some((res, idx))
+        let (res, import) = to_type_ns(module_def)?;
+        Some((res, idx, import))
     }
 }
 
-fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
-    let res = match per_ns.take_values()? {
+fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> {
+    let (def, import) = per_ns.take_values_import()?;
+    let res = match def {
         ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
         ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
         ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
@@ -855,11 +871,12 @@ fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
         | ModuleDefId::MacroId(_)
         | ModuleDefId::ModuleId(_) => return None,
     };
-    Some(res)
+    Some((res, import))
 }
 
-fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
-    let res = match per_ns.take_types()? {
+fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+    let (def, _, import) = per_ns.take_types_full()?;
+    let res = match def {
         ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
         ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
 
@@ -875,7 +892,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
         | ModuleDefId::StaticId(_)
         | ModuleDefId::ModuleId(_) => return None,
     };
-    Some(res)
+    Some((res, import))
 }
 
 type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
@@ -892,13 +909,13 @@ impl ScopeNames {
         }
     }
     fn add_per_ns(&mut self, name: &Name, def: PerNs) {
-        if let &Some((ty, _)) = &def.types {
+        if let &Some((ty, _, _)) = &def.types {
             self.add(name, ScopeDef::ModuleDef(ty))
         }
-        if let &Some((def, _)) = &def.values {
+        if let &Some((def, _, _)) = &def.values {
             self.add(name, ScopeDef::ModuleDef(def))
         }
-        if let &Some((mac, _)) = &def.macros {
+        if let &Some((mac, _, _)) = &def.macros {
             self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)))
         }
         if def.is_none() {
diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs
index 6047f770d4d..3770103cda5 100644
--- a/crates/hir-def/src/src.rs
+++ b/crates/hir-def/src/src.rs
@@ -5,8 +5,8 @@ use la_arena::ArenaMap;
 use syntax::ast;
 
 use crate::{
-    db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Macro2Loc, MacroRulesLoc,
-    ProcMacroLoc,
+    db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Lookup, Macro2Loc,
+    MacroRulesLoc, ProcMacroLoc, UseId,
 };
 
 pub trait HasSource {
@@ -83,3 +83,18 @@ pub trait HasChildSource<ChildId> {
     type Value;
     fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
 }
+
+impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
+    type Value = ast::UseTree;
+    fn child_source(
+        &self,
+        db: &dyn DefDatabase,
+    ) -> InFile<ArenaMap<la_arena::Idx<ast::UseTree>, Self::Value>> {
+        let loc = &self.lookup(db);
+        let use_ = &loc.id.item_tree(db)[loc.id.value];
+        InFile::new(
+            loc.id.file_id(),
+            use_.use_tree_source_map(db, loc.id.file_id()).into_iter().collect(),
+        )
+    }
+}
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 4c918e55b92..0ec2422b30c 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -342,14 +342,7 @@ fn inner_attributes(
             ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
             ast::Module(it) => it.item_list()?.syntax().clone(),
             ast::BlockExpr(it) => {
-                use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT};
-                // Block expressions accept outer and inner attributes, but only when they are the outer
-                // expression of an expression statement or the final expression of another block expression.
-                let may_carry_attributes = matches!(
-                    it.syntax().parent().map(|it| it.kind()),
-                     Some(BLOCK_EXPR | EXPR_STMT)
-                );
-                if !may_carry_attributes {
+                if !it.may_carry_attributes() {
                     return None
                 }
                 syntax.clone()
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 1f1e20f49e3..4be55126b86 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -37,7 +37,7 @@ use either::Either;
 use syntax::{
     algo::{self, skip_trivia_token},
     ast::{self, AstNode, HasDocComments},
-    AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken,
+    AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
 };
 
 use crate::{
@@ -544,7 +544,7 @@ impl MacroCallKind {
         };
 
         let range = match kind {
-            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
             MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
                 // FIXME: should be the range of the macro name, not the whole derive
                 // FIXME: handle `cfg_attr`
@@ -642,6 +642,8 @@ impl ExpansionInfo {
         db: &dyn db::ExpandDatabase,
         item: Option<ast::Item>,
         token: InFile<&SyntaxToken>,
+        // FIXME: use this for range mapping, so that we can resolve inline format args
+        _relative_token_offset: Option<TextSize>,
     ) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
         assert_eq!(token.file_id, self.arg.file_id);
         let token_id_in_attr_input = if let Some(item) = item {
@@ -840,9 +842,6 @@ impl<N: AstIdNode> AstId<N> {
 pub type ErasedAstId = InFile<ErasedFileAstId>;
 
 impl ErasedAstId {
-    pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
-        self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
-    }
     pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
         db.ast_id_map(self.file_id).get_raw(self.value)
     }
@@ -1054,16 +1053,6 @@ impl InFile<SyntaxToken> {
             }
         }
     }
-
-    pub fn ancestors_with_macros(
-        self,
-        db: &dyn db::ExpandDatabase,
-    ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
-        self.value.parent().into_iter().flat_map({
-            let file_id = self.file_id;
-            move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db)
-        })
-    }
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs
index eec57ba3f80..967e028bfb1 100644
--- a/crates/hir-ty/src/builder.rs
+++ b/crates/hir-ty/src/builder.rs
@@ -17,7 +17,8 @@ use smallvec::SmallVec;
 use crate::{
     consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
     to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
-    GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+    GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
+    TyKind,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -79,9 +80,9 @@ impl<D> TyBuilder<D> {
         let expected_kind = &self.param_kinds[self.vec.len()];
 
         let arg_kind = match arg.data(Interner) {
-            chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
-            chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
-            chalk_ir::GenericArgData::Const(c) => {
+            GenericArgData::Ty(_) => ParamKind::Type,
+            GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
+            GenericArgData::Const(c) => {
                 let c = c.data(Interner);
                 ParamKind::Const(c.ty.clone())
             }
@@ -139,8 +140,8 @@ impl<D> TyBuilder<D> {
 
     fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
         match (a.data(Interner), e) {
-            (chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
-            | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (),
+            (GenericArgData::Ty(_), ParamKind::Type)
+            | (GenericArgData::Const(_), ParamKind::Const(_)) => (),
             _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds),
         }
     }
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 1c0f7b08da8..0348680e5da 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -1,7 +1,7 @@
 //! Constant evaluation details
 
 use base_db::CrateId;
-use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
+use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
 use hir_def::{
     hir::Expr,
     path::Path,
@@ -120,7 +120,7 @@ pub fn unknown_const(ty: Ty) -> Const {
 }
 
 pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
-    GenericArgData::Const(unknown_const(ty)).intern(Interner)
+    unknown_const(ty).cast(Interner)
 }
 
 /// Interns a constant scalar with the given type
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 666955fa1c3..7ad3659a4f6 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1203,6 +1203,27 @@ fn destructing_assignment() {
         "#,
         5,
     );
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let (mut a, mut b) = (2, 5);
+        (a, b) = (b, a);
+        a * 10 + b
+    };
+        "#,
+        52,
+    );
+    check_number(
+        r#"
+    struct Point { x: i32, y: i32 }
+    const GOAL: i32 = {
+        let mut p = Point { x: 5, y: 6 };
+        (p.x, _) = (p.y, p.x);
+        p.x * 10 + p.y
+    };
+        "#,
+        66,
+    );
 }
 
 #[test]
@@ -1432,6 +1453,30 @@ fn from_trait() {
     );
 }
 
+#[test]
+fn closure_clone() {
+    check_number(
+        r#"
+//- minicore: clone, fn
+struct S(u8);
+
+impl Clone for S(u8) {
+    fn clone(&self) -> S {
+        S(self.0 + 5)
+    }
+}
+
+const GOAL: u8 = {
+    let s = S(3);
+    let cl = move || s;
+    let cl = cl.clone();
+    cl().0
+}
+    "#,
+        8,
+    );
+}
+
 #[test]
 fn builtin_derive_macro() {
     check_number(
@@ -2396,14 +2441,14 @@ fn const_loop() {
 fn const_transfer_memory() {
     check_number(
         r#"
-    //- minicore: slice, index, coerce_unsized
+    //- minicore: slice, index, coerce_unsized, option
     const A1: &i32 = &1;
     const A2: &i32 = &10;
     const A3: [&i32; 3] = [&1, &2, &100];
-    const A4: (i32, &i32) = (1, &1000);
-    const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1;
+    const A4: (i32, &i32, Option<&i32>) = (1, &1000, Some(&10000));
+    const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1 + *A4.2.unwrap_or(&5);
     "#,
-        1111,
+        11111,
     );
 }
 
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 9f9a56ffab0..cbca0e801d4 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -75,7 +75,7 @@ fn walk_unsafe(
         Expr::Path(path) => {
             let resolver = resolver_for_expr(db.upcast(), def, current);
             let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
-            if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
+            if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
                 if db.static_data(id).mutable {
                     unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
                 }
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 1b4ee4613d6..f6d6b00d740 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -1809,6 +1809,25 @@ impl HirDisplay for Path {
             }
         }
 
+        // Convert trait's `Self` bound back to the surface syntax. Note there is no associated
+        // trait, so there can only be one path segment that `has_self_type`. The `Self` type
+        // itself can contain further qualified path through, which will be handled by recursive
+        // `hir_fmt`s.
+        //
+        // `trait_mod::Trait<Self = type_mod::Type, Args>::Assoc`
+        // =>
+        // `<type_mod::Type as trait_mod::Trait<Args>>::Assoc`
+        let trait_self_ty = self.segments().iter().find_map(|seg| {
+            let generic_args = seg.args_and_bindings?;
+            generic_args.has_self_type.then(|| &generic_args.args[0])
+        });
+        if let Some(ty) = trait_self_ty {
+            write!(f, "<")?;
+            ty.hir_fmt(f)?;
+            write!(f, " as ")?;
+            // Now format the path of the trait...
+        }
+
         for (seg_idx, segment) in self.segments().iter().enumerate() {
             if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
                 write!(f, "::")?;
@@ -1840,15 +1859,12 @@ impl HirDisplay for Path {
                     return Ok(());
                 }
 
-                write!(f, "<")?;
                 let mut first = true;
-                for arg in generic_args.args.iter() {
+                // Skip the `Self` bound if exists. It's handled outside the loop.
+                for arg in &generic_args.args[generic_args.has_self_type as usize..] {
                     if first {
                         first = false;
-                        if generic_args.has_self_type {
-                            // FIXME: Convert to `<Ty as Trait>` form.
-                            write!(f, "Self = ")?;
-                        }
+                        write!(f, "<")?;
                     } else {
                         write!(f, ", ")?;
                     }
@@ -1857,6 +1873,7 @@ impl HirDisplay for Path {
                 for binding in generic_args.bindings.iter() {
                     if first {
                         first = false;
+                        write!(f, "<")?;
                     } else {
                         write!(f, ", ")?;
                     }
@@ -1872,9 +1889,20 @@ impl HirDisplay for Path {
                         }
                     }
                 }
-                write!(f, ">")?;
+
+                // There may be no generic arguments to print, in case of a trait having only a
+                // single `Self` bound which is converted to `<Ty as Trait>::Assoc`.
+                if !first {
+                    write!(f, ">")?;
+                }
+
+                // Current position: `<Ty as Trait<Args>|`
+                if generic_args.has_self_type {
+                    write!(f, ">")?;
+                }
             }
         }
+
         Ok(())
     }
 }
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index b4915dbf0f9..0fb4934444b 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -1017,7 +1017,7 @@ impl<'a> InferenceContext<'a> {
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
         let (resolution, unresolved) = if value_ns {
             match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
-                Some(ResolveValueResult::ValueNs(value)) => match value {
+                Some(ResolveValueResult::ValueNs(value, _)) => match value {
                     ValueNs::EnumVariantId(var) => {
                         let substs = ctx.substs_from_path(path, var.into(), true);
                         let ty = self.db.ty(var.parent.into());
@@ -1033,12 +1033,14 @@ impl<'a> InferenceContext<'a> {
                     ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
                     _ => return (self.err_ty(), None),
                 },
-                Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)),
+                Some(ResolveValueResult::Partial(typens, unresolved, _)) => {
+                    (typens, Some(unresolved))
+                }
                 None => return (self.err_ty(), None),
             }
         } else {
             match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
-                Some(it) => it,
+                Some((it, idx, _)) => (it, idx),
                 None => return (self.err_ty(), None),
             }
         };
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 1781f6c58f1..23efe616f4f 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -322,7 +322,7 @@ impl InferenceContext<'_> {
             Expr::Path(p) => {
                 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
                 if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
-                    if let ResolveValueResult::ValueNs(v) = r {
+                    if let ResolveValueResult::ValueNs(v, _) = r {
                         if let ValueNs::LocalBinding(b) = v {
                             return Some(HirPlace { local: b, projections: vec![] });
                         }
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 8cbdae62526..8b352141084 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -5,9 +5,7 @@ use std::{
     mem,
 };
 
-use chalk_ir::{
-    cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
-};
+use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
 use hir_def::{
     generics::TypeOrConstParamData,
     hir::{
@@ -750,7 +748,7 @@ impl InferenceContext<'_> {
                     self.resolve_associated_type_with_params(
                         self_ty,
                         self.resolve_ops_index_output(),
-                        &[GenericArgData::Ty(index_ty).intern(Interner)],
+                        &[index_ty.cast(Interner)],
                     )
                 } else {
                     self.err_ty()
@@ -1721,16 +1719,13 @@ impl InferenceContext<'_> {
         for (id, data) in def_generics.iter().skip(substs.len()) {
             match data {
                 TypeOrConstParamData::TypeParamData(_) => {
-                    substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner))
-                }
-                TypeOrConstParamData::ConstParamData(_) => {
-                    substs.push(
-                        GenericArgData::Const(self.table.new_const_var(
-                            self.db.const_param_ty(ConstParamId::from_unchecked(id)),
-                        ))
-                        .intern(Interner),
-                    )
+                    substs.push(self.table.new_type_var().cast(Interner))
                 }
+                TypeOrConstParamData::ConstParamData(_) => substs.push(
+                    self.table
+                        .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id)))
+                        .cast(Interner),
+                ),
             }
         }
         assert_eq!(substs.len(), total_len);
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 79d9e21e797..2a51c84db3a 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -61,8 +61,8 @@ impl InferenceContext<'_> {
                 self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
 
             match value_or_partial {
-                ResolveValueResult::ValueNs(it) => (it, None),
-                ResolveValueResult::Partial(def, remaining_index) => self
+                ResolveValueResult::ValueNs(it, _) => (it, None),
+                ResolveValueResult::Partial(def, remaining_index, _) => self
                     .resolve_assoc_item(def, path, remaining_index, id)
                     .map(|(it, substs)| (it, Some(substs)))?,
             }
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 0fb71135b4d..0a68a9f3b58 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -10,7 +10,6 @@ use chalk_solve::infer::ParameterEnaVariableExt;
 use either::Either;
 use ena::unify::UnifyKey;
 use hir_expand::name;
-use stdx::never;
 use triomphe::Arc;
 
 use super::{InferOk, InferResult, InferenceContext, TypeError};
@@ -92,15 +91,10 @@ pub(crate) fn unify(
     let vars = Substitution::from_iter(
         Interner,
         tys.binders.iter(Interner).map(|it| match &it.kind {
-            chalk_ir::VariableKind::Ty(_) => {
-                GenericArgData::Ty(table.new_type_var()).intern(Interner)
-            }
-            chalk_ir::VariableKind::Lifetime => {
-                GenericArgData::Ty(table.new_type_var()).intern(Interner)
-            } // FIXME: maybe wrong?
-            chalk_ir::VariableKind::Const(ty) => {
-                GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
-            }
+            chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner),
+            // FIXME: maybe wrong?
+            chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner),
+            chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
         }),
     );
     let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
@@ -111,10 +105,10 @@ pub(crate) fn unify(
     // default any type vars that weren't unified back to their original bound vars
     // (kind of hacky)
     let find_var = |iv| {
-        vars.iter(Interner).position(|v| match v.interned() {
-            chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
-            chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
-            chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+        vars.iter(Interner).position(|v| match v.data(Interner) {
+            GenericArgData::Ty(ty) => ty.inference_var(Interner),
+            GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+            GenericArgData::Const(c) => c.inference_var(Interner),
         } == Some(iv))
     };
     let fallback = |iv, kind, default, binder| match kind {
@@ -149,6 +143,9 @@ pub(crate) struct InferenceTable<'a> {
     var_unification_table: ChalkInferenceTable,
     type_variable_table: Vec<TypeVariableFlags>,
     pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
+    /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on
+    /// temporary allocations.
+    resolve_obligations_buffer: Vec<Canonicalized<InEnvironment<Goal>>>,
 }
 
 pub(crate) struct InferenceTableSnapshot {
@@ -165,6 +162,7 @@ impl<'a> InferenceTable<'a> {
             var_unification_table: ChalkInferenceTable::new(),
             type_variable_table: Vec::new(),
             pending_obligations: Vec::new(),
+            resolve_obligations_buffer: Vec::new(),
         }
     }
 
@@ -516,10 +514,10 @@ impl<'a> InferenceTable<'a> {
     pub(crate) fn resolve_obligations_as_possible(&mut self) {
         let _span = profile::span("resolve_obligations_as_possible");
         let mut changed = true;
-        let mut obligations = Vec::new();
-        while changed {
-            changed = false;
+        let mut obligations = mem::take(&mut self.resolve_obligations_buffer);
+        while mem::take(&mut changed) {
             mem::swap(&mut self.pending_obligations, &mut obligations);
+
             for canonicalized in obligations.drain(..) {
                 if !self.check_changed(&canonicalized) {
                     self.pending_obligations.push(canonicalized);
@@ -534,6 +532,8 @@ impl<'a> InferenceTable<'a> {
                 self.register_obligation_in_env(uncanonical);
             }
         }
+        self.resolve_obligations_buffer = obligations;
+        self.resolve_obligations_buffer.clear();
     }
 
     pub(crate) fn fudge_inference<T: TypeFoldable<Interner>>(
@@ -611,9 +611,9 @@ impl<'a> InferenceTable<'a> {
     fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool {
         canonicalized.free_vars.iter().any(|var| {
             let iv = match var.data(Interner) {
-                chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
-                chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
-                chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+                GenericArgData::Ty(ty) => ty.inference_var(Interner),
+                GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+                GenericArgData::Const(c) => c.inference_var(Interner),
             }
             .expect("free var is not inference var");
             if self.var_unification_table.probe_var(iv).is_some() {
@@ -690,14 +690,10 @@ impl<'a> InferenceTable<'a> {
             .fill(|it| {
                 let arg = match it {
                     ParamKind::Type => self.new_type_var(),
-                    ParamKind::Const(ty) => {
-                        never!("Tuple with const parameter");
-                        return GenericArgData::Const(self.new_const_var(ty.clone()))
-                            .intern(Interner);
-                    }
+                    ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
                 };
                 arg_tys.push(arg.clone());
-                GenericArgData::Ty(arg).intern(Interner)
+                arg.cast(Interner)
             })
             .build();
 
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index b3ca2a22258..405bb001b5d 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -52,12 +52,14 @@ use hir_expand::name;
 use la_arena::{Arena, Idx};
 use mir::{MirEvalError, VTableMap};
 use rustc_hash::FxHashSet;
+use syntax::ast::{make, ConstArg};
 use traits::FnTrait;
 use triomphe::Arc;
 use utils::Generics;
 
 use crate::{
-    consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics,
+    consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable,
+    utils::generics,
 };
 
 pub use autoderef::autoderef;
@@ -719,3 +721,16 @@ where
     value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
     collector.placeholders.into_iter().collect()
 }
+
+pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstArg> {
+    if let ConstValue::Concrete(c) = &konst.interned().value {
+        match c.interned {
+            ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => {
+                return Some(cid.source(db.upcast()));
+            }
+            ConstScalar::Unknown => return None,
+            _ => (),
+        }
+    }
+    Some(make::expr_const_value(konst.display(db).to_string().as_str()))
+}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 2837f400bce..9a61f153599 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -58,10 +58,9 @@ use crate::{
         InTypeConstIdMetadata,
     },
     AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
-    FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig,
-    ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait,
-    ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
-    TyKind, WhereClause,
+    FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
+    QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
+    Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
 };
 
 #[derive(Debug)]
@@ -213,6 +212,19 @@ impl<'a> TyLoweringContext<'a> {
         self.lower_ty_ext(type_ref).0
     }
 
+    pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
+        const_or_path_to_chalk(
+            self.db,
+            self.resolver,
+            self.owner,
+            const_type,
+            const_ref,
+            self.type_param_mode,
+            || self.generics(),
+            self.in_binders,
+        )
+    }
+
     fn generics(&self) -> Generics {
         generics(
             self.db.upcast(),
@@ -242,17 +254,7 @@ impl<'a> TyLoweringContext<'a> {
             }
             TypeRef::Array(inner, len) => {
                 let inner_ty = self.lower_ty(inner);
-                let const_len = const_or_path_to_chalk(
-                    self.db,
-                    self.resolver,
-                    self.owner,
-                    TyBuilder::usize(),
-                    len,
-                    self.type_param_mode,
-                    || self.generics(),
-                    self.in_binders,
-                );
-
+                let const_len = self.lower_const(len, TyBuilder::usize());
                 TyKind::Array(inner_ty, const_len).intern(Interner)
             }
             TypeRef::Slice(inner) => {
@@ -391,11 +393,9 @@ impl<'a> TyLoweringContext<'a> {
                 let ty = {
                     let macro_call = macro_call.to_node(self.db.upcast());
                     let resolver = |path| {
-                        self.resolver.resolve_path_as_macro(
-                            self.db.upcast(),
-                            &path,
-                            Some(MacroSubNs::Bang),
-                        )
+                        self.resolver
+                            .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
+                            .map(|(it, _)| it)
                     };
                     match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
                     {
@@ -447,7 +447,7 @@ impl<'a> TyLoweringContext<'a> {
             return None;
         }
         let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
-            Some((it, None)) => it,
+            Some((it, None, _)) => it,
             _ => return None,
         };
         match resolution {
@@ -627,7 +627,7 @@ impl<'a> TyLoweringContext<'a> {
             return self.lower_ty_relative_path(ty, res, path.segments());
         }
 
-        let (resolution, remaining_index) =
+        let (resolution, remaining_index, _) =
             match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
                 Some(it) => it,
                 None => return (TyKind::Error.intern(Interner), None),
@@ -847,18 +847,7 @@ impl<'a> TyLoweringContext<'a> {
                         arg,
                         &mut (),
                         |_, type_ref| self.lower_ty(type_ref),
-                        |_, c, ty| {
-                            const_or_path_to_chalk(
-                                self.db,
-                                self.resolver,
-                                self.owner,
-                                ty,
-                                c,
-                                self.type_param_mode,
-                                || self.generics(),
-                                self.in_binders,
-                            )
-                        },
+                        |_, const_ref, ty| self.lower_const(const_ref, ty),
                     ) {
                         had_explicit_args = true;
                         substs.push(x);
@@ -1604,24 +1593,35 @@ pub(crate) fn generic_defaults_query(
             .iter()
             .enumerate()
             .map(|(idx, (id, p))| {
-                let p = match p {
-                    TypeOrConstParamData::TypeParamData(p) => p,
-                    TypeOrConstParamData::ConstParamData(_) => {
-                        // FIXME: implement const generic defaults
-                        let val = unknown_const_as_generic(
-                            db.const_param_ty(ConstParamId::from_unchecked(id)),
-                        );
-                        return make_binders(db, &generic_params, val);
+                match p {
+                    TypeOrConstParamData::TypeParamData(p) => {
+                        let mut ty = p
+                            .default
+                            .as_ref()
+                            .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
+                        // Each default can only refer to previous parameters.
+                        // Type variable default referring to parameter coming
+                        // after it is forbidden (FIXME: report diagnostic)
+                        ty = fallback_bound_vars(ty, idx, parent_start_idx);
+                        crate::make_binders(db, &generic_params, ty.cast(Interner))
                     }
-                };
-                let mut ty =
-                    p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
-
-                // Each default can only refer to previous parameters.
-                // Type variable default referring to parameter coming
-                // after it is forbidden (FIXME: report diagnostic)
-                ty = fallback_bound_vars(ty, idx, parent_start_idx);
-                crate::make_binders(db, &generic_params, ty.cast(Interner))
+                    TypeOrConstParamData::ConstParamData(p) => {
+                        let mut val = p.default.as_ref().map_or_else(
+                            || {
+                                unknown_const_as_generic(
+                                    db.const_param_ty(ConstParamId::from_unchecked(id)),
+                                )
+                            },
+                            |c| {
+                                let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
+                                c.cast(Interner)
+                            },
+                        );
+                        // Each default can only refer to previous parameters, see above.
+                        val = fallback_bound_vars(val, idx, parent_start_idx);
+                        make_binders(db, &generic_params, val)
+                    }
+                }
             })
             // FIXME: use `Arc::from_iter` when it becomes available
             .collect::<Vec<_>>(),
@@ -1643,9 +1643,7 @@ pub(crate) fn generic_defaults_recover(
             .iter_id()
             .map(|id| {
                 let val = match id {
-                    Either::Left(_) => {
-                        GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
-                    }
+                    Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
                     Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
                 };
                 crate::make_binders(db, &generic_params, val)
@@ -1991,16 +1989,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
         }
     };
     Some(match (arg, kind) {
-        (GenericArg::Type(type_ref), ParamKind::Type) => {
-            let ty = for_type(this, type_ref);
-            GenericArgData::Ty(ty).intern(Interner)
-        }
-        (GenericArg::Const(c), ParamKind::Const(c_ty)) => {
-            GenericArgData::Const(for_const(this, c, c_ty)).intern(Interner)
-        }
-        (GenericArg::Const(_), ParamKind::Type) => {
-            GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
-        }
+        (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner),
+        (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
+        (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
         (GenericArg::Type(t), ParamKind::Const(c_ty)) => {
             // We want to recover simple idents, which parser detects them
             // as types. Maybe here is not the best place to do it, but
@@ -2010,9 +2001,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
                 if p.kind == PathKind::Plain {
                     if let [n] = p.segments() {
                         let c = ConstRef::Path(n.clone());
-                        return Some(
-                            GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
-                        );
+                        return Some(for_const(this, &c, c_ty).cast(Interner));
                     }
                 }
             }
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 9e30eed56f3..3944feb128c 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -10,7 +10,7 @@ use std::{
 };
 
 use base_db::{CrateId, FileId};
-use chalk_ir::Mutability;
+use chalk_ir::{cast::Cast, Mutability};
 use either::Either;
 use hir_def::{
     builtin_type::BuiltinType,
@@ -40,8 +40,8 @@ use crate::{
     name, static_lifetime,
     traits::FnTrait,
     utils::{detect_variant_from_bytes, ClosureSubst},
-    CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
-    Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+    CallableDefId, ClosureId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution,
+    TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
 };
 
 use super::{
@@ -2007,7 +2007,28 @@ impl Evaluator<'_> {
                     }
                 }
                 AdtId::UnionId(_) => (),
-                AdtId::EnumId(_) => (),
+                AdtId::EnumId(e) => {
+                    if let Some((variant, layout)) = detect_variant_from_bytes(
+                        &layout,
+                        self.db,
+                        self.trait_env.clone(),
+                        self.read_memory(addr, layout.size.bytes_usize())?,
+                        e,
+                    ) {
+                        let ev = EnumVariantId { parent: e, local_id: variant };
+                        for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() {
+                            let offset = layout.fields.offset(i).bytes_usize();
+                            let ty = ty.clone().substitute(Interner, subst);
+                            self.patch_addresses(
+                                patch_map,
+                                old_vtable,
+                                addr.offset(offset),
+                                &ty,
+                                locals,
+                            )?;
+                        }
+                    }
+                }
             },
             TyKind::Tuple(_, subst) => {
                 for (id, ty) in subst.iter(Interner).enumerate() {
@@ -2248,7 +2269,7 @@ impl Evaluator<'_> {
                     interval: args_for_target[0].interval.slice(0..self.ptr_size()),
                     ty: ty.clone(),
                 };
-                let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
+                let ty = ty.clone().cast(Interner);
                 let generics_for_target = Substitution::from_iter(
                     Interner,
                     generic_args.iter(Interner).enumerate().map(|(i, it)| {
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index b2e29fd34b5..52943e97ac0 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -136,7 +136,10 @@ impl Evaluator<'_> {
                     not_supported!("wrong generic arg kind for clone");
                 };
                 // Clone has special impls for tuples and function pointers
-                if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) {
+                if matches!(
+                    self_ty.kind(Interner),
+                    TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..)
+                ) {
                     self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
                     return Ok(true);
                 }
@@ -167,32 +170,26 @@ impl Evaluator<'_> {
                 return destination
                     .write_from_interval(self, Interval { addr, size: destination.size });
             }
+            TyKind::Closure(id, subst) => {
+                let [arg] = args else {
+                    not_supported!("wrong arg count for clone");
+                };
+                let addr = Address::from_bytes(arg.get(self)?)?;
+                let (closure_owner, _) = self.db.lookup_intern_closure((*id).into());
+                let infer = self.db.infer(closure_owner);
+                let (captures, _) = infer.closure_info(id);
+                let layout = self.layout(&self_ty)?;
+                let ty_iter = captures.iter().map(|c| c.ty(subst));
+                self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
+            }
             TyKind::Tuple(_, subst) => {
                 let [arg] = args else {
                     not_supported!("wrong arg count for clone");
                 };
                 let addr = Address::from_bytes(arg.get(self)?)?;
                 let layout = self.layout(&self_ty)?;
-                for (i, ty) in subst.iter(Interner).enumerate() {
-                    let ty = ty.assert_ty_ref(Interner);
-                    let size = self.layout(ty)?.size.bytes_usize();
-                    let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
-                    let arg = IntervalAndTy {
-                        interval: Interval { addr: tmp, size: self.ptr_size() },
-                        ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone())
-                            .intern(Interner),
-                    };
-                    let offset = layout.fields.offset(i).bytes_usize();
-                    self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
-                    self.exec_clone(
-                        def,
-                        &[arg],
-                        ty.clone(),
-                        locals,
-                        destination.slice(offset..offset + size),
-                        span,
-                    )?;
-                }
+                let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone());
+                self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
             }
             _ => {
                 self.exec_fn_with_args(
@@ -209,6 +206,37 @@ impl Evaluator<'_> {
         Ok(())
     }
 
+    fn exec_clone_for_fields(
+        &mut self,
+        ty_iter: impl Iterator<Item = Ty>,
+        layout: Arc<Layout>,
+        addr: Address,
+        def: FunctionId,
+        locals: &Locals,
+        destination: Interval,
+        span: MirSpan,
+    ) -> Result<()> {
+        for (i, ty) in ty_iter.enumerate() {
+            let size = self.layout(&ty)?.size.bytes_usize();
+            let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
+            let arg = IntervalAndTy {
+                interval: Interval { addr: tmp, size: self.ptr_size() },
+                ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner),
+            };
+            let offset = layout.fields.offset(i).bytes_usize();
+            self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
+            self.exec_clone(
+                def,
+                &[arg],
+                ty,
+                locals,
+                destination.slice(offset..offset + size),
+                span,
+            )?;
+        }
+        Ok(())
+    }
+
     fn exec_alloc_fn(
         &mut self,
         alloc_fn: &str,
@@ -473,6 +501,38 @@ impl Evaluator<'_> {
                 self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
                 Ok(())
             }
+            "getenv" => {
+                let [name] = args else {
+                    return Err(MirEvalError::TypeError("libc::write args are not provided"));
+                };
+                let mut name_buf = vec![];
+                let name = {
+                    let mut index = Address::from_bytes(name.get(self)?)?;
+                    loop {
+                        let byte = self.read_memory(index, 1)?[0];
+                        index = index.offset(1);
+                        if byte == 0 {
+                            break;
+                        }
+                        name_buf.push(byte);
+                    }
+                    String::from_utf8_lossy(&name_buf)
+                };
+                let value = self.db.crate_graph()[self.crate_id].env.get(&name);
+                match value {
+                    None => {
+                        // Write null as fail
+                        self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
+                    }
+                    Some(mut value) => {
+                        value.push('\0');
+                        let addr = self.heap_allocate(value.len(), 1)?;
+                        self.write_memory(addr, value.as_bytes())?;
+                        self.write_memory(destination.addr, &addr.to_bytes())?;
+                    }
+                }
+                Ok(())
+            }
             _ => not_supported!("unknown external function {as_str}"),
         }
     }
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 46165cf3d69..ff30dc6dade 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -729,6 +729,48 @@ fn main() {
     )
 }
 
+#[test]
+fn posix_getenv() {
+    check_pass(
+        r#"
+//- /main.rs env:foo=bar
+
+type c_char = u8;
+
+extern "C" {
+    pub fn getenv(s: *const c_char) -> *mut c_char;
+}
+
+fn should_not_reach() {
+    _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+    let result = getenv(b"foo\0" as *const _);
+    if *result != b'b' {
+        should_not_reach();
+    }
+    let result = (result as usize + 1) as *const c_char;
+    if *result != b'a' {
+        should_not_reach();
+    }
+    let result = (result as usize + 1) as *const c_char;
+    if *result != b'r' {
+        should_not_reach();
+    }
+    let result = (result as usize + 1) as *const c_char;
+    if *result != 0 {
+        should_not_reach();
+    }
+    let result = getenv(b"not found\0" as *const _);
+    if result as usize != 0 {
+        should_not_reach();
+    }
+}
+"#,
+    );
+}
+
 #[test]
 fn posix_tls() {
     check_pass(
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 718df8331e2..51cf882d053 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -15,7 +15,7 @@ use hir_def::{
     path::Path,
     resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
     AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
-    TraitId, TypeOrConstParamId,
+    Lookup, TraitId, TypeOrConstParamId,
 };
 use hir_expand::name::Name;
 use la_arena::ArenaMap;
@@ -372,7 +372,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
         match &self.body.exprs[expr_id] {
             Expr::Missing => {
                 if let DefWithBodyId::FunctionId(f) = self.owner {
-                    let assoc = self.db.lookup_intern_function(f);
+                    let assoc = f.lookup(self.db.upcast());
                     if let ItemContainerId::TraitId(t) = assoc.container {
                         let name = &self.db.function_data(f).name;
                         return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
@@ -1244,6 +1244,41 @@ impl<'ctx> MirLowerCtx<'ctx> {
         }
     }
 
+    fn lower_destructing_assignment(
+        &mut self,
+        mut current: BasicBlockId,
+        lhs: ExprId,
+        rhs: Place,
+        span: MirSpan,
+    ) -> Result<Option<BasicBlockId>> {
+        match &self.body.exprs[lhs] {
+            Expr::Tuple { exprs, is_assignee_expr: _ } => {
+                for (i, expr) in exprs.iter().enumerate() {
+                    let Some(c) = self.lower_destructing_assignment(
+                        current,
+                        *expr,
+                        rhs.project(ProjectionElem::TupleOrClosureField(i)),
+                        span,
+                    )? else {
+                        return Ok(None);
+                    };
+                    current = c;
+                }
+                Ok(Some(current))
+            }
+            Expr::Underscore => Ok(Some(current)),
+            _ => {
+                let Some((lhs_place, current)) =
+                    self.lower_expr_as_place(current, lhs, false)?
+                else {
+                    return Ok(None);
+                };
+                self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span);
+                Ok(Some(current))
+            }
+        }
+    }
+
     fn lower_assignment(
         &mut self,
         current: BasicBlockId,
@@ -1259,6 +1294,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
         if matches!(&self.body.exprs[lhs], Expr::Underscore) {
             return Ok(Some(current));
         }
+        if matches!(
+            &self.body.exprs[lhs],
+            Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. }
+        ) {
+            let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?;
+            let temp = Place::from(temp);
+            self.push_assignment(current, temp.clone(), rhs_op.into(), span);
+            return self.lower_destructing_assignment(current, lhs, temp, span);
+        }
         let Some((lhs_place, current)) =
             self.lower_expr_as_place(current, lhs, false)?
         else {
@@ -1308,14 +1352,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
                     .resolve_path_in_value_ns(self.db.upcast(), c)
                     .ok_or_else(unresolved_name)?;
                 match pr {
-                    ResolveValueResult::ValueNs(v) => {
+                    ResolveValueResult::ValueNs(v, _) => {
                         if let ValueNs::ConstId(c) = v {
                             self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
                         } else {
                             not_supported!("bad path in range pattern");
                         }
                     }
-                    ResolveValueResult::Partial(_, _) => {
+                    ResolveValueResult::Partial(_, _, _) => {
                         not_supported!("associated constants in range pattern")
                     }
                 }
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 3354cbd76a0..1cdfd919742 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -323,7 +323,7 @@ impl MirLowerCtx<'_> {
                                 break 'b (c, x.1);
                             }
                         }
-                        if let ResolveValueResult::ValueNs(v) = pr {
+                        if let ResolveValueResult::ValueNs(v, _) = pr {
                             if let ValueNs::ConstId(c) = v {
                                 break 'b (c, Substitution::empty(Interner));
                             }
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 0f2fb2c8118..121e5a0a249 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -3,18 +3,19 @@
 use hir_def::{
     attr::{AttrsWithOwner, Documentation},
     item_scope::ItemInNs,
-    path::ModPath,
-    resolver::HasResolver,
-    AttrDefId, GenericParamId, ModuleDefId,
+    path::{ModPath, Path},
+    per_ns::Namespace,
+    resolver::{HasResolver, Resolver, TypeNs},
+    AssocItemId, AttrDefId, GenericParamId, ModuleDefId,
 };
-use hir_expand::hygiene::Hygiene;
+use hir_expand::{hygiene::Hygiene, name::Name};
 use hir_ty::db::HirDatabase;
 use syntax::{ast, AstNode};
 
 use crate::{
-    Adt, AssocItem, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, Impl,
-    LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias,
-    TypeParam, Union, Variant,
+    Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field,
+    Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait,
+    TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
 };
 
 pub trait HasAttrs {
@@ -25,14 +26,14 @@ pub trait HasAttrs {
         db: &dyn HirDatabase,
         link: &str,
         ns: Option<Namespace>,
-    ) -> Option<ModuleDef>;
+    ) -> Option<DocLinkDef>;
 }
 
-#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
-pub enum Namespace {
-    Types,
-    Values,
-    Macros,
+/// Subset of `ide_db::Definition` that doc links can resolve to.
+pub enum DocLinkDef {
+    ModuleDef(ModuleDef),
+    Field(Field),
+    SelfType(Trait),
 }
 
 macro_rules! impl_has_attrs {
@@ -46,9 +47,14 @@ macro_rules! impl_has_attrs {
                 let def = AttrDefId::$def_id(self.into());
                 db.attrs(def).docs()
             }
-            fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
+            fn resolve_doc_path(
+                self,
+                db: &dyn HirDatabase,
+                link: &str,
+                ns: Option<Namespace>
+            ) -> Option<DocLinkDef> {
                 let def = AttrDefId::$def_id(self.into());
-                resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
+                resolve_doc_path(db, def, link, ns)
             }
         }
     )*};
@@ -79,7 +85,12 @@ macro_rules! impl_has_attrs_enum {
             fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
                 $enum::$variant(self).docs(db)
             }
-            fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
+            fn resolve_doc_path(
+                self,
+                db: &dyn HirDatabase,
+                link: &str,
+                ns: Option<Namespace>
+            ) -> Option<DocLinkDef> {
                 $enum::$variant(self).resolve_doc_path(db, link, ns)
             }
         }
@@ -111,7 +122,7 @@ impl HasAttrs for AssocItem {
         db: &dyn HirDatabase,
         link: &str,
         ns: Option<Namespace>,
-    ) -> Option<ModuleDef> {
+    ) -> Option<DocLinkDef> {
         match self {
             AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
             AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
@@ -147,9 +158,9 @@ impl HasAttrs for ExternCrateDecl {
         db: &dyn HirDatabase,
         link: &str,
         ns: Option<Namespace>,
-    ) -> Option<ModuleDef> {
+    ) -> Option<DocLinkDef> {
         let def = AttrDefId::ExternCrateId(self.into());
-        resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
+        resolve_doc_path(db, def, link, ns)
     }
 }
 
@@ -159,7 +170,7 @@ fn resolve_doc_path(
     def: AttrDefId,
     link: &str,
     ns: Option<Namespace>,
-) -> Option<ModuleDefId> {
+) -> Option<DocLinkDef> {
     let resolver = match def {
         AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
         AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
@@ -184,8 +195,107 @@ fn resolve_doc_path(
         .resolver(db.upcast()),
     };
 
-    let modpath = {
-        // FIXME: this is not how we should get a mod path here
+    let mut modpath = modpath_from_str(db, link)?;
+
+    let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
+    if resolved.is_none() {
+        let last_name = modpath.pop_segment()?;
+        resolve_assoc_or_field(db, resolver, modpath, last_name, ns)
+    } else {
+        let def = match ns {
+            Some(Namespace::Types) => resolved.take_types(),
+            Some(Namespace::Values) => resolved.take_values(),
+            Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
+            None => resolved.iter_items().next().map(|(it, _)| match it {
+                ItemInNs::Types(it) => it,
+                ItemInNs::Values(it) => it,
+                ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
+            }),
+        };
+        Some(DocLinkDef::ModuleDef(def?.into()))
+    }
+}
+
+fn resolve_assoc_or_field(
+    db: &dyn HirDatabase,
+    resolver: Resolver,
+    path: ModPath,
+    name: Name,
+    ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+    let path = Path::from_known_path_with_no_generic(path);
+    // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the
+    // trait itself.
+    let base_def = resolver.resolve_path_in_type_ns_fully(db.upcast(), &path)?;
+
+    let ty = match base_def {
+        TypeNs::SelfType(id) => Impl::from(id).self_ty(db),
+        TypeNs::GenericParam(_) => {
+            // Even if this generic parameter has some trait bounds, rustdoc doesn't
+            // resolve `name` to trait items.
+            return None;
+        }
+        TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db),
+        TypeNs::EnumVariantId(id) => {
+            // Enum variants don't have path candidates.
+            let variant = Variant::from(id);
+            return resolve_field(db, variant.into(), name, ns);
+        }
+        TypeNs::TypeAliasId(id) => {
+            let alias = TypeAlias::from(id);
+            if alias.as_assoc_item(db).is_some() {
+                // We don't normalize associated type aliases, so we have nothing to
+                // resolve `name` to.
+                return None;
+            }
+            alias.ty(db)
+        }
+        TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db),
+        TypeNs::TraitId(id) => {
+            // Doc paths in this context may only resolve to an item of this trait
+            // (i.e. no items of its supertraits), so we need to handle them here
+            // independently of others.
+            return db.trait_data(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
+                let def = match *assoc_id {
+                    AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
+                    AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
+                    AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
+                };
+                DocLinkDef::ModuleDef(def)
+            });
+        }
+        TypeNs::TraitAliasId(_) => {
+            // XXX: Do these get resolved?
+            return None;
+        }
+    };
+
+    // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
+    // precedence over fields.
+
+    let variant_def = match ty.as_adt()? {
+        Adt::Struct(it) => it.into(),
+        Adt::Union(it) => it.into(),
+        Adt::Enum(_) => return None,
+    };
+    resolve_field(db, variant_def, name, ns)
+}
+
+fn resolve_field(
+    db: &dyn HirDatabase,
+    def: VariantDef,
+    name: Name,
+    ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+    if let Some(Namespace::Types | Namespace::Macros) = ns {
+        return None;
+    }
+    def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field)
+}
+
+fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> {
+    // FIXME: this is not how we should get a mod path here.
+    let try_get_modpath = |link: &str| {
         let ast_path = ast::SourceFile::parse(&format!("type T = {link};"))
             .syntax_node()
             .descendants()
@@ -193,23 +303,20 @@ fn resolve_doc_path(
         if ast_path.syntax().text() != link {
             return None;
         }
-        ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
+        ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())
     };
 
-    let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
-    let resolved = if resolved.is_none() {
-        resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
-    } else {
-        resolved
-    };
-    match ns {
-        Some(Namespace::Types) => resolved.take_types(),
-        Some(Namespace::Values) => resolved.take_values(),
-        Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
-        None => resolved.iter_items().next().map(|it| match it {
-            ItemInNs::Types(it) => it,
-            ItemInNs::Values(it) => it,
-            ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
-        }),
+    let full = try_get_modpath(link);
+    if full.is_some() {
+        return full;
     }
+
+    // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can
+    // resolve doc paths like `TupleStruct::0`.
+    // FIXME: Find a better way to handle these.
+    let (base, maybe_tuple_field) = link.rsplit_once("::")?;
+    let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?);
+    let mut modpath = try_get_modpath(base)?;
+    modpath.push_segment(tuple_field);
+    Some(modpath)
 }
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 9dfb98e459b..ac171026d5d 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -8,7 +8,6 @@ use hir_def::{
     type_ref::{TypeBound, TypeRef},
     AdtId, GenericDefId,
 };
-use hir_expand::name;
 use hir_ty::{
     display::{
         write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@@ -19,8 +18,9 @@ use hir_ty::{
 
 use crate::{
     Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field,
-    Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct,
-    Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
+    Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam,
+    Static, Struct, Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam,
+    Union, Variant,
 };
 
 impl HirDisplay for Function {
@@ -57,37 +57,21 @@ impl HirDisplay for Function {
 
         f.write_char('(')?;
 
-        let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty {
-            TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
-            TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
-            {
-                f.write_char('&')?;
-                if let Some(lifetime) = lifetime {
-                    write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
-                }
-                if let hir_def::type_ref::Mutability::Mut = mut_ {
-                    f.write_str("mut ")?;
-                }
-                f.write_str("self")
-            }
-            _ => {
-                f.write_str("self: ")?;
-                ty.hir_fmt(f)
-            }
-        };
-
         let mut first = true;
+        let mut skip_self = 0;
+        if let Some(self_param) = self.self_param(db) {
+            self_param.hir_fmt(f)?;
+            first = false;
+            skip_self = 1;
+        }
+
         // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
-        for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
+        for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
             let local = param.as_local(db).map(|it| it.name(db));
             if !first {
                 f.write_str(", ")?;
             } else {
                 first = false;
-                if local == Some(name!(self)) {
-                    write_self_param(type_ref, f)?;
-                    continue;
-                }
             }
             match local {
                 Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
@@ -137,6 +121,31 @@ impl HirDisplay for Function {
     }
 }
 
+impl HirDisplay for SelfParam {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        let data = f.db.function_data(self.func);
+        let param = data.params.first().unwrap();
+        match &**param {
+            TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
+            TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
+            {
+                f.write_char('&')?;
+                if let Some(lifetime) = lifetime {
+                    write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
+                }
+                if let hir_def::type_ref::Mutability::Mut = mut_ {
+                    f.write_str("mut ")?;
+                }
+                f.write_str("self")
+            }
+            ty => {
+                f.write_str("self: ")?;
+                ty.hir_fmt(f)
+            }
+        }
+    }
+}
+
 impl HirDisplay for Adt {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         match self {
@@ -357,6 +366,11 @@ fn write_generic_params(
                     delim(f)?;
                     write!(f, "const {}: ", name.display(f.db.upcast()))?;
                     c.ty.hir_fmt(f)?;
+
+                    if let Some(default) = &c.default {
+                        f.write_str(" = ")?;
+                        write!(f, "{}", default.display(f.db.upcast()))?;
+                    }
                 }
             }
         }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bf041b61f2f..512fe7e0428 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -63,12 +63,13 @@ use hir_ty::{
     all_super_traits, autoderef,
     consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
     diagnostics::BodyValidationDiagnostic,
+    known_const_to_ast,
     layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
     method_resolution::{self, TyFingerprint},
     mir::{self, interpret_mir},
     primitive::UintTy,
     traits::FnTrait,
-    AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
+    AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
     GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
     TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
     WhereClause,
@@ -87,7 +88,7 @@ use triomphe::Arc;
 use crate::db::{DefDatabase, HirDatabase};
 
 pub use crate::{
-    attrs::{HasAttrs, Namespace},
+    attrs::{DocLinkDef, HasAttrs},
     diagnostics::{
         AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
         IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
@@ -121,6 +122,7 @@ pub use {
         lang_item::LangItem,
         nameres::{DefMap, ModuleSource},
         path::{ModPath, PathKind},
+        per_ns::Namespace,
         type_ref::{Mutability, TypeRef},
         visibility::Visibility,
         // FIXME: This is here since some queries take it as input that are used
@@ -719,20 +721,18 @@ fn emit_def_diagnostic_(
 ) {
     match diag {
         DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
-            let decl = declaration.to_node(db.upcast());
+            let decl = declaration.to_ptr(db.upcast());
             acc.push(
                 UnresolvedModule {
-                    decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
+                    decl: InFile::new(declaration.file_id, decl),
                     candidates: candidates.clone(),
                 }
                 .into(),
             )
         }
         DefDiagnosticKind::UnresolvedExternCrate { ast } => {
-            let item = ast.to_node(db.upcast());
-            acc.push(
-                UnresolvedExternCrate { decl: InFile::new(ast.file_id, AstPtr::new(&item)) }.into(),
-            );
+            let item = ast.to_ptr(db.upcast());
+            acc.push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into());
         }
 
         DefDiagnosticKind::UnresolvedImport { id, index } => {
@@ -747,14 +747,10 @@ fn emit_def_diagnostic_(
         }
 
         DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
-            let item = ast.to_node(db.upcast());
+            let item = ast.to_ptr(db.upcast());
             acc.push(
-                InactiveCode {
-                    node: ast.with_value(SyntaxNodePtr::new(&item).into()),
-                    cfg: cfg.clone(),
-                    opts: opts.clone(),
-                }
-                .into(),
+                InactiveCode { node: ast.with_value(item), cfg: cfg.clone(), opts: opts.clone() }
+                    .into(),
             );
         }
         DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
@@ -1273,7 +1269,7 @@ impl Adt {
             .fill(|x| {
                 let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
                 match x {
-                    ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+                    ParamKind::Type => r.cast(Interner),
                     ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
                 }
             })
@@ -2096,14 +2092,6 @@ impl SelfParam {
             .unwrap_or(Access::Owned)
     }
 
-    pub fn display(self, db: &dyn HirDatabase) -> &'static str {
-        match self.access(db) {
-            Access::Shared => "&self",
-            Access::Exclusive => "&mut self",
-            Access::Owned => "self",
-        }
-    }
-
     pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
         let InFile { file_id, value } = Function::from(self.func).source(db)?;
         value
@@ -3142,12 +3130,8 @@ impl TypeParam {
     }
 
     pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
-        let params = db.generic_defaults(self.id.parent());
-        let local_idx = hir_ty::param_idx(db, self.id.into())?;
+        let ty = generic_arg_from_param(db, self.id.into())?;
         let resolver = self.id.parent().resolver(db.upcast());
-        let ty = params.get(local_idx)?.clone();
-        let subst = TyBuilder::placeholder_subst(db, self.id.parent());
-        let ty = ty.substitute(Interner, &subst);
         match ty.data(Interner) {
             GenericArgData::Ty(it) => {
                 Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
@@ -3209,6 +3193,19 @@ impl ConstParam {
     pub fn ty(self, db: &dyn HirDatabase) -> Type {
         Type::new(db, self.id.parent(), db.const_param_ty(self.id))
     }
+
+    pub fn default(self, db: &dyn HirDatabase) -> Option<ast::ConstArg> {
+        let arg = generic_arg_from_param(db, self.id.into())?;
+        known_const_to_ast(arg.constant(Interner)?, db)
+    }
+}
+
+fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg> {
+    let params = db.generic_defaults(id.parent);
+    let local_idx = hir_ty::param_idx(db, id)?;
+    let ty = params.get(local_idx)?.clone();
+    let subst = TyBuilder::placeholder_subst(db, id.parent);
+    Some(ty.substitute(Interner, &subst))
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -3716,7 +3713,7 @@ impl Type {
             .fill(|x| {
                 let r = it.next().unwrap();
                 match x {
-                    ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+                    ParamKind::Type => r.cast(Interner),
                     ParamKind::Const(ty) => {
                         // FIXME: this code is not covered in tests.
                         unknown_const_as_generic(ty.clone())
@@ -3749,9 +3746,7 @@ impl Type {
             .fill(|it| {
                 // FIXME: this code is not covered in tests.
                 match it {
-                    ParamKind::Type => {
-                        GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner)
-                    }
+                    ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner),
                     ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
                 }
             })
@@ -4414,14 +4409,13 @@ impl Callable {
             Other => CallableKind::Other,
         }
     }
-    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> {
+    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> {
         let func = match self.callee {
             Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
             _ => return None,
         };
-        let src = func.lookup(db.upcast()).source(db.upcast());
-        let param_list = src.value.param_list()?;
-        Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone())))
+        let func = Function { id: func };
+        Some((func.self_param(db)?, self.ty.derived(self.sig.params()[0].clone())))
     }
     pub fn n_params(&self) -> usize {
         self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index e99d2984c36..b8d4ecd4414 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -170,6 +170,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.is_derive_annotated(item)
     }
 
+    /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the
+    /// expansion. `token_to_map` should be a token from the `speculative args` node.
     pub fn speculative_expand(
         &self,
         actual_macro_call: &ast::MacroCall,
@@ -179,6 +181,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map)
     }
 
+    /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
+    /// expansion. `token_to_map` should be a token from the `speculative args` node.
     pub fn speculative_expand_attr_macro(
         &self,
         actual_macro_call: &ast::Item,
@@ -201,14 +205,22 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         )
     }
 
-    /// Descend the token into macrocalls to its first mapped counterpart.
-    pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
-        self.imp.descend_into_macros_single(token)
+    /// Descend the token into its macro call if it is part of one, returning the token in the
+    /// expansion that it is associated with. If `offset` points into the token's range, it will
+    /// be considered for the mapping in case of inline format args.
+    pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
+        self.imp.descend_into_macros_single(token, offset)
     }
 
-    /// Descend the token into macrocalls to all its mapped counterparts.
-    pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
-        self.imp.descend_into_macros(token)
+    /// Descend the token into its macro call if it is part of one, returning the tokens in the
+    /// expansion that it is associated with. If `offset` points into the token's range, it will
+    /// be considered for the mapping in case of inline format args.
+    pub fn descend_into_macros(
+        &self,
+        token: SyntaxToken,
+        offset: TextSize,
+    ) -> SmallVec<[SyntaxToken; 1]> {
+        self.imp.descend_into_macros(token, offset)
     }
 
     /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
@@ -217,12 +229,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
     pub fn descend_into_macros_with_same_text(
         &self,
         token: SyntaxToken,
+        offset: TextSize,
     ) -> SmallVec<[SyntaxToken; 1]> {
-        self.imp.descend_into_macros_with_same_text(token)
+        self.imp.descend_into_macros_with_same_text(token, offset)
     }
 
-    pub fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken {
-        self.imp.descend_into_macros_with_kind_preference(token)
+    pub fn descend_into_macros_with_kind_preference(
+        &self,
+        token: SyntaxToken,
+        offset: TextSize,
+    ) -> SyntaxToken {
+        self.imp.descend_into_macros_with_kind_preference(token, offset)
     }
 
     /// Maps a node down by mapping its first and last token down.
@@ -606,7 +623,7 @@ impl<'db> SemanticsImpl<'db> {
         let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
             resolver
                 .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
-                .map(|it| macro_id_to_def_id(self.db.upcast(), it))
+                .map(|(it, _)| macro_id_to_def_id(self.db.upcast(), it))
         })?;
         hir_expand::db::expand_speculative(
             self.db.upcast(),
@@ -665,7 +682,7 @@ impl<'db> SemanticsImpl<'db> {
         };
 
         if first == last {
-            self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
+            self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| {
                 if let Some(node) = value.parent_ancestors().find_map(N::cast) {
                     res.push(node)
                 }
@@ -674,7 +691,7 @@ impl<'db> SemanticsImpl<'db> {
         } else {
             // Descend first and last token, then zip them to look for the node they belong to
             let mut scratch: SmallVec<[_; 1]> = smallvec![];
-            self.descend_into_macros_impl(first, &mut |token| {
+            self.descend_into_macros_impl(first, 0.into(), &mut |token| {
                 scratch.push(token);
                 false
             });
@@ -682,6 +699,7 @@ impl<'db> SemanticsImpl<'db> {
             let mut scratch = scratch.into_iter();
             self.descend_into_macros_impl(
                 last,
+                0.into(),
                 &mut |InFile { value: last, file_id: last_fid }| {
                     if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
                         if first_fid == last_fid {
@@ -705,19 +723,27 @@ impl<'db> SemanticsImpl<'db> {
         res
     }
 
-    fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+    fn descend_into_macros(
+        &self,
+        token: SyntaxToken,
+        offset: TextSize,
+    ) -> SmallVec<[SyntaxToken; 1]> {
         let mut res = smallvec![];
-        self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
+        self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
             res.push(value);
             false
         });
         res
     }
 
-    fn descend_into_macros_with_same_text(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+    fn descend_into_macros_with_same_text(
+        &self,
+        token: SyntaxToken,
+        offset: TextSize,
+    ) -> SmallVec<[SyntaxToken; 1]> {
         let text = token.text();
         let mut res = smallvec![];
-        self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
+        self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
             if value.text() == text {
                 res.push(value);
             }
@@ -729,7 +755,11 @@ impl<'db> SemanticsImpl<'db> {
         res
     }
 
-    fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken {
+    fn descend_into_macros_with_kind_preference(
+        &self,
+        token: SyntaxToken,
+        offset: TextSize,
+    ) -> SyntaxToken {
         let fetch_kind = |token: &SyntaxToken| match token.parent() {
             Some(node) => match node.kind() {
                 kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => {
@@ -741,7 +771,7 @@ impl<'db> SemanticsImpl<'db> {
         };
         let preferred_kind = fetch_kind(&token);
         let mut res = None;
-        self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
+        self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
             if fetch_kind(&value) == preferred_kind {
                 res = Some(value);
                 true
@@ -755,9 +785,9 @@ impl<'db> SemanticsImpl<'db> {
         res.unwrap_or(token)
     }
 
-    fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
+    fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
         let mut res = token.clone();
-        self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
+        self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
             res = value;
             true
         });
@@ -767,9 +797,13 @@ impl<'db> SemanticsImpl<'db> {
     fn descend_into_macros_impl(
         &self,
         token: SyntaxToken,
+        // FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange
+        // mapping, specifically for node downmapping
+        offset: TextSize,
         f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
     ) {
         let _p = profile::span("descend_into_macros");
+        let relative_token_offset = token.text_range().start().checked_sub(offset);
         let parent = match token.parent() {
             Some(it) => it,
             None => return,
@@ -796,7 +830,12 @@ impl<'db> SemanticsImpl<'db> {
                     self.cache(value, file_id);
                 }
 
-                let mapped_tokens = expansion_info.map_token_down(self.db.upcast(), item, token)?;
+                let mapped_tokens = expansion_info.map_token_down(
+                    self.db.upcast(),
+                    item,
+                    token,
+                    relative_token_offset,
+                )?;
                 let len = stack.len();
 
                 // requeue the tokens we got from mapping our current token down
@@ -943,7 +982,7 @@ impl<'db> SemanticsImpl<'db> {
         offset: TextSize,
     ) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
         node.token_at_offset(offset)
-            .map(move |token| self.descend_into_macros(token))
+            .map(move |token| self.descend_into_macros(token, offset))
             .map(|descendants| {
                 descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
             })
@@ -1683,6 +1722,14 @@ impl SemanticsScope<'_> {
             |name, id| cb(name, id.into()),
         )
     }
+
+    pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
+        self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
+    }
+
+    pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ {
+        self.resolver.extern_crate_decls_in_scope(self.db.upcast())
+    }
 }
 
 #[derive(Debug)]
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 3499daf1140..f29fb1edf00 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -487,7 +487,7 @@ impl SourceAnalyzer {
         let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
         self.resolver
             .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang))
-            .map(|it| it.into())
+            .map(|(it, _)| it.into())
     }
 
     pub(crate) fn resolve_bind_pat_to_const(
@@ -760,7 +760,7 @@ impl SourceAnalyzer {
         let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
             self.resolver
                 .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang))
-                .map(|it| macro_id_to_def_id(db.upcast(), it))
+                .map(|(it, _)| macro_id_to_def_id(db.upcast(), it))
         })?;
         Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
     }
@@ -966,6 +966,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro(
 ) -> Option<Macro> {
     resolver
         .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr))
+        .map(|(it, _)| it)
         .map(Into::into)
 }
 
@@ -983,7 +984,7 @@ fn resolve_hir_path_(
                 res.map(|ty_ns| (ty_ns, path.segments().first()))
             }
             None => {
-                let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
+                let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
                 match remaining_idx {
                     Some(remaining_idx) => {
                         if remaining_idx + 1 == path.segments().len() {
@@ -1067,7 +1068,7 @@ fn resolve_hir_path_(
     let macros = || {
         resolver
             .resolve_path_as_macro(db.upcast(), path.mod_path()?, None)
-            .map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
+            .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into())))
     };
 
     if prefer_value_ns { values().or_else(types) } else { types().or_else(values) }
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index 43d957412bc..ca7874c3683 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -2,8 +2,10 @@
 
 use base_db::FileRange;
 use hir_def::{
-    src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId,
-    ModuleDefId, ModuleId, TraitId,
+    item_scope::ItemInNs,
+    src::{HasChildSource, HasSource},
+    AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
+    TraitId,
 };
 use hir_expand::{HirFileId, InFile};
 use hir_ty::db::HirDatabase;
@@ -167,6 +169,40 @@ impl<'a> SymbolCollector<'a> {
             self.collect_from_impl(impl_id);
         }
 
+        // Record renamed imports.
+        // In case it imports multiple items under different namespaces we just pick one arbitrarily
+        // for now.
+        for id in scope.imports() {
+            let loc = id.import.lookup(self.db.upcast());
+            loc.id.item_tree(self.db.upcast());
+            let source = id.import.child_source(self.db.upcast());
+            let Some(use_tree_src) = source.value.get(id.idx) else { continue };
+            let Some(rename) = use_tree_src.rename() else { continue };
+            let Some(name) = rename.name() else { continue };
+
+            let res = scope.fully_resolve_import(self.db.upcast(), id);
+            res.iter_items().for_each(|(item, _)| {
+                let def = match item {
+                    ItemInNs::Types(def) | ItemInNs::Values(def) => def,
+                    ItemInNs::Macros(def) => ModuleDefId::from(def),
+                }
+                .into();
+                let dec_loc = DeclarationLocation {
+                    hir_file_id: source.file_id,
+                    ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
+                    name_ptr: SyntaxNodePtr::new(name.syntax()),
+                };
+
+                self.symbols.push(FileSymbol {
+                    name: name.text().into(),
+                    def,
+                    container_name: self.current_container_name.clone(),
+                    loc: dec_loc,
+                    is_alias: false,
+                });
+            });
+        }
+
         for const_id in scope.unnamed_consts() {
             self.collect_from_body(const_id);
         }
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 6aca716bb60..c0e5429a22c 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
         check_assist(
             add_missing_default_members,
             r#"
-struct Bar<const: N: bool> {
+struct Bar<const N: usize> {
     bar: [i32, N]
 }
 
@@ -439,7 +439,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
     $0
 }"#,
             r#"
-struct Bar<const: N: bool> {
+struct Bar<const N: usize> {
     bar: [i32, N]
 }
 
@@ -483,6 +483,107 @@ impl<X> Foo<42, {20 + 22}, X> for () {
         )
     }
 
+    #[test]
+    fn test_const_substitution_with_defaults() {
+        check_assist(
+            add_missing_default_members,
+            r#"
+trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
+    fn get_n(&self) -> usize { N }
+    fn get_m(&self) -> bool { M }
+    fn get_p(&self) -> char { P }
+    fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
+}
+
+impl<X> Foo<X> for () {
+    $0
+}"#,
+            r#"
+trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
+    fn get_n(&self) -> usize { N }
+    fn get_m(&self) -> bool { M }
+    fn get_p(&self) -> char { P }
+    fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
+}
+
+impl<X> Foo<X> for () {
+    $0fn get_n(&self) -> usize { 42 }
+
+    fn get_m(&self) -> bool { false }
+
+    fn get_p(&self) -> char { 'a' }
+
+    fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] }
+}"#,
+        );
+    }
+
+    #[test]
+    fn test_const_substitution_with_defaults_2() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+mod m {
+    pub const LEN: usize = 42;
+    pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+        fn get_t(&self) -> T;
+    }
+}
+
+impl m::Foo for () {
+    $0
+}"#,
+            r#"
+mod m {
+    pub const LEN: usize = 42;
+    pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+        fn get_t(&self) -> T;
+    }
+}
+
+impl m::Foo for () {
+    fn get_t(&self) -> [bool; m::LEN] {
+        ${0:todo!()}
+    }
+}"#,
+        )
+    }
+
+    #[test]
+    fn test_const_substitution_with_defaults_3() {
+        check_assist(
+            add_missing_default_members,
+            r#"
+mod m {
+    pub const VAL: usize = 0;
+
+    pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
+        fn get_n(&self) -> usize { N }
+        fn get_m(&self) -> usize { M }
+    }
+}
+
+impl m::Foo for () {
+    $0
+}"#,
+            r#"
+mod m {
+    pub const VAL: usize = 0;
+
+    pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
+        fn get_n(&self) -> usize { N }
+        fn get_m(&self) -> usize { M }
+    }
+}
+
+impl m::Foo for () {
+    $0fn get_n(&self) -> usize { {40 + 2} }
+
+    fn get_m(&self) -> usize { {m::VAL + 1} }
+}"#,
+        )
+    }
+
     #[test]
     fn test_cursor_after_empty_impl_def() {
         check_assist(
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index 57cfa17cc8e..66bc2f6dadc 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -1,6 +1,10 @@
 use std::collections::VecDeque;
 
-use syntax::ast::{self, AstNode};
+use syntax::{
+    ast::{self, AstNode, Expr::BinExpr},
+    ted::{self, Position},
+    SyntaxKind,
+};
 
 use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
 
@@ -23,121 +27,117 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
 // }
 // ```
 pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
-    let op = expr.op_kind()?;
-    let op_range = expr.op_token()?.text_range();
+    let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
+    let op = bin_expr.op_kind()?;
+    let op_range = bin_expr.op_token()?.text_range();
 
-    let opposite_op = match op {
-        ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||",
-        ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&",
-        _ => return None,
-    };
-
-    let cursor_in_range = op_range.contains_range(ctx.selection_trimmed());
-    if !cursor_in_range {
+    // Is the cursor on the expression's logical operator?
+    if !op_range.contains_range(ctx.selection_trimmed()) {
         return None;
     }
 
-    let mut expr = expr;
-
     // Walk up the tree while we have the same binary operator
-    while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
-        match expr.op_kind() {
+    while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) {
+        match parent_expr.op_kind() {
             Some(parent_op) if parent_op == op => {
-                expr = parent_expr;
+                bin_expr = parent_expr;
             }
             _ => break,
         }
     }
 
-    let mut expr_stack = vec![expr.clone()];
-    let mut terms = Vec::new();
-    let mut op_ranges = Vec::new();
+    let op = bin_expr.op_kind()?;
+    let inv_token = match op {
+        ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2,
+        ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2,
+        _ => return None,
+    };
 
-    // Find all the children with the same binary operator
-    while let Some(expr) = expr_stack.pop() {
-        let mut traverse_bin_expr_arm = |expr| {
-            if let ast::Expr::BinExpr(bin_expr) = expr {
-                if let Some(expr_op) = bin_expr.op_kind() {
-                    if expr_op == op {
-                        expr_stack.push(bin_expr);
-                    } else {
-                        terms.push(ast::Expr::BinExpr(bin_expr));
-                    }
+    let demorganed = bin_expr.clone_subtree().clone_for_update();
+
+    ted::replace(demorganed.op_token()?, ast::make::token(inv_token));
+    let mut exprs = VecDeque::from(vec![
+        (bin_expr.lhs()?, demorganed.lhs()?),
+        (bin_expr.rhs()?, demorganed.rhs()?),
+    ]);
+
+    while let Some((expr, dm)) = exprs.pop_front() {
+        if let BinExpr(bin_expr) = &expr {
+            if let BinExpr(cbin_expr) = &dm {
+                if op == bin_expr.op_kind()? {
+                    ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token));
+                    exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
+                    exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
                 } else {
-                    terms.push(ast::Expr::BinExpr(bin_expr));
+                    let mut inv = invert_boolean_expression(expr);
+                    if inv.needs_parens_in(dm.syntax().parent()?) {
+                        inv = ast::make::expr_paren(inv).clone_for_update();
+                    }
+                    ted::replace(dm.syntax(), inv.syntax());
                 }
             } else {
-                terms.push(expr);
+                return None;
             }
-        };
-
-        op_ranges.extend(expr.op_token().map(|t| t.text_range()));
-        traverse_bin_expr_arm(expr.lhs()?);
-        traverse_bin_expr_arm(expr.rhs()?);
+        } else {
+            let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update();
+            if inv.needs_parens_in(dm.syntax().parent()?) {
+                inv = ast::make::expr_paren(inv).clone_for_update();
+            }
+            ted::replace(dm.syntax(), inv.syntax());
+        }
     }
 
+    let dm_lhs = demorganed.lhs()?;
+
     acc.add(
         AssistId("apply_demorgan", AssistKind::RefactorRewrite),
         "Apply De Morgan's law",
         op_range,
         |edit| {
-            terms.sort_by_key(|t| t.syntax().text_range().start());
-            let mut terms = VecDeque::from(terms);
-
-            let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
-
+            let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
             let neg_expr = paren_expr
                 .clone()
                 .and_then(|paren_expr| paren_expr.syntax().parent())
                 .and_then(ast::PrefixExpr::cast)
                 .and_then(|prefix_expr| {
-                    if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not {
+                    if prefix_expr.op_kind()? == ast::UnaryOp::Not {
                         Some(prefix_expr)
                     } else {
                         None
                     }
                 });
 
-            for op_range in op_ranges {
-                edit.replace(op_range, opposite_op);
-            }
-
             if let Some(paren_expr) = paren_expr {
-                for term in terms {
-                    let range = term.syntax().text_range();
-                    let not_term = invert_boolean_expression(term);
-
-                    edit.replace(range, not_term.syntax().text());
-                }
-
                 if let Some(neg_expr) = neg_expr {
                     cov_mark::hit!(demorgan_double_negation);
-                    edit.replace(neg_expr.op_token().unwrap().text_range(), "");
+                    edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into());
                 } else {
                     cov_mark::hit!(demorgan_double_parens);
-                    edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
+                    ted::insert_all_raw(
+                        Position::before(dm_lhs.syntax()),
+                        vec![
+                            syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)),
+                            syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)),
+                        ],
+                    );
+
+                    ted::append_child_raw(
+                        demorganed.syntax(),
+                        syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)),
+                    );
+
+                    edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into());
                 }
             } else {
-                if let Some(lhs) = terms.pop_front() {
-                    let lhs_range = lhs.syntax().text_range();
-                    let not_lhs = invert_boolean_expression(lhs);
-
-                    edit.replace(lhs_range, format!("!({not_lhs}"));
-                }
-
-                if let Some(rhs) = terms.pop_back() {
-                    let rhs_range = rhs.syntax().text_range();
-                    let not_rhs = invert_boolean_expression(rhs);
-
-                    edit.replace(rhs_range, format!("{not_rhs})"));
-                }
-
-                for term in terms {
-                    let term_range = term.syntax().text_range();
-                    let not_term = invert_boolean_expression(term);
-                    edit.replace(term_range, not_term.to_string());
-                }
+                ted::insert_all_raw(
+                    Position::before(dm_lhs.syntax()),
+                    vec![
+                        syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)),
+                        syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)),
+                    ],
+                );
+                ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN));
+                edit.replace_ast(bin_expr, demorganed);
             }
         },
     )
@@ -145,9 +145,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
     use super::*;
+    use crate::tests::{check_assist, check_assist_not_applicable};
 
     #[test]
     fn demorgan_handles_leq() {
@@ -213,7 +212,7 @@ fn f() { !(S <= S || S < S) }
     #[test]
     fn demorgan_doesnt_double_negation() {
         cov_mark::check!(demorgan_double_negation);
-        check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
+        check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { !x && !x }")
     }
 
     #[test]
@@ -222,13 +221,38 @@ fn f() { !(S <= S || S < S) }
         check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
     }
 
-    // https://github.com/rust-lang/rust-analyzer/issues/10963
+    // FIXME : This needs to go.
+    // // https://github.com/rust-lang/rust-analyzer/issues/10963
+    // #[test]
+    // fn demorgan_doesnt_hang() {
+    //     check_assist(
+    //         apply_demorgan,
+    //         "fn f() { 1 || 3 &&$0 4 || 5 }",
+    //         "fn f() { !(!1 || !3 || !4) || 5 }",
+    //     )
+    // }
+
     #[test]
-    fn demorgan_doesnt_hang() {
+    fn demorgan_keep_pars_for_op_precedence() {
         check_assist(
             apply_demorgan,
-            "fn f() { 1 || 3 &&$0 4 || 5 }",
-            "fn f() { !(!1 || !3 || !4) || 5 }",
+            "fn main() {
+    let _ = !(!a ||$0 !(b || c));
+}
+",
+            "fn main() {
+    let _ = a && (b || c);
+}
+",
+        );
+    }
+
+    #[test]
+    fn demorgan_removes_pars_in_eq_precedence() {
+        check_assist(
+            apply_demorgan,
+            "fn() { let x = a && !(!b |$0| !c); }",
+            "fn() { let x = a && b && c; }",
         )
     }
 }
diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index fe1cb6fce36..76f021ed912 100644
--- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -161,9 +161,9 @@ fn process_struct_name_reference(
     let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
     // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point.
     let full_path =
-        path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap();
+        path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last()?;
 
-    if full_path.segment().unwrap().name_ref()? != *name_ref {
+    if full_path.segment()?.name_ref()? != *name_ref {
         // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the
         // struct we want to edit.
         return None;
diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index dcb96ab8af4..7d0e424769e 100644
--- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -58,7 +58,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
                     return None;
                 }
 
-                let bound_ident = pat.fields().next().unwrap();
+                let bound_ident = pat.fields().next()?;
                 if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
                     return None;
                 }
@@ -108,6 +108,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
 
     then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
 
+    let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
+
+    let end_of_then = then_block_items.syntax().last_child_or_token()?;
+    let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
+        end_of_then.prev_sibling_or_token()?
+    } else {
+        end_of_then
+    };
+
     let target = if_expr.syntax().text_range();
     acc.add(
         AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
@@ -141,16 +150,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
                 }
             };
 
-            let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
-
-            let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
-            let end_of_then =
-                if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
-                    end_of_then.prev_sibling_or_token().unwrap()
-                } else {
-                    end_of_then
-                };
-
             let then_statements = replacement
                 .children_with_tokens()
                 .chain(
diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index 4f3b6e0c287..c3d925cb26c 100644
--- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -48,7 +48,7 @@ pub(crate) fn extract_expressions_from_format_string(
     let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
 
     let expanded_t = ast::String::cast(
-        ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone()),
+        ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()),
     )?;
     if !is_format_string(&expanded_t) {
         return None;
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index b8b781ea48d..ea7a21e77a4 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -750,7 +750,7 @@ impl FunctionBody {
                         .descendants_with_tokens()
                         .filter_map(SyntaxElement::into_token)
                         .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
-                        .flat_map(|t| sema.descend_into_macros(t))
+                        .flat_map(|t| sema.descend_into_macros(t, 0.into()))
                         .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
                 }
             }
@@ -810,7 +810,7 @@ impl FunctionBody {
                         (true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
                     },
                     ast::ConstParam(cp) => {
-                        (true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db)))
+                        (true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db)))
                     },
                     ast::ConstBlockPat(cbp) => {
                         let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
@@ -1385,31 +1385,30 @@ enum FlowHandler {
 
 impl FlowHandler {
     fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
-        match &fun.control_flow.kind {
-            None => FlowHandler::None,
-            Some(flow_kind) => {
-                let action = flow_kind.clone();
-                if let FunType::Unit = ret_ty {
-                    match flow_kind {
-                        FlowKind::Return(None)
-                        | FlowKind::Break(_, None)
-                        | FlowKind::Continue(_) => FlowHandler::If { action },
-                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
-                            FlowHandler::IfOption { action }
-                        }
-                        FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
-                    }
-                } else {
-                    match flow_kind {
-                        FlowKind::Return(None)
-                        | FlowKind::Break(_, None)
-                        | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
-                        FlowKind::Return(_) | FlowKind::Break(_, _) => {
-                            FlowHandler::MatchResult { err: action }
-                        }
-                        FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
-                    }
+        if fun.contains_tail_expr {
+            return FlowHandler::None;
+        }
+        let Some(action) = fun.control_flow.kind.clone() else {
+            return FlowHandler::None;
+        };
+
+        if let FunType::Unit = ret_ty {
+            match action {
+                FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
+                    FlowHandler::If { action }
                 }
+                FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action },
+                FlowKind::Try { kind } => FlowHandler::Try { kind },
+            }
+        } else {
+            match action {
+                FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
+                    FlowHandler::MatchOption { none: action }
+                }
+                FlowKind::Return(_) | FlowKind::Break(_, _) => {
+                    FlowHandler::MatchResult { err: action }
+                }
+                FlowKind::Try { kind } => FlowHandler::Try { kind },
             }
         }
     }
@@ -1654,11 +1653,7 @@ impl Function {
 
     fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
         let fun_ty = self.return_type(ctx);
-        let handler = if self.contains_tail_expr {
-            FlowHandler::None
-        } else {
-            FlowHandler::from_ret_ty(self, &fun_ty)
-        };
+        let handler = FlowHandler::from_ret_ty(self, &fun_ty);
         let ret_ty = match &handler {
             FlowHandler::None => {
                 if matches!(fun_ty, FunType::Unit) {
@@ -1728,11 +1723,7 @@ fn make_body(
     fun: &Function,
 ) -> ast::BlockExpr {
     let ret_ty = fun.return_type(ctx);
-    let handler = if fun.contains_tail_expr {
-        FlowHandler::None
-    } else {
-        FlowHandler::from_ret_ty(fun, &ret_ty)
-    };
+    let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
 
     let block = match &fun.body {
         FunctionBody::Expr(expr) => {
@@ -4471,7 +4462,7 @@ async fn foo() -> Result<(), ()> {
 "#,
             r#"
 async fn foo() -> Result<(), ()> {
-    fun_name().await?
+    fun_name().await
 }
 
 async fn $0fun_name() -> Result<(), ()> {
@@ -4690,7 +4681,7 @@ fn $0fun_name() {
         check_assist(
             extract_function,
             r#"
-//- minicore: result
+//- minicore: result, try
 fn foo() -> Result<(), i64> {
     $0Result::<i32, i64>::Ok(0)?;
     Ok(())$0
@@ -4698,7 +4689,7 @@ fn foo() -> Result<(), i64> {
 "#,
             r#"
 fn foo() -> Result<(), i64> {
-    fun_name()?
+    fun_name()
 }
 
 fn $0fun_name() -> Result<(), i64> {
@@ -5753,6 +5744,34 @@ fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
         );
     }
 
+    #[test]
+    fn tail_expr_no_extra_control_flow() {
+        check_assist(
+            extract_function,
+            r#"
+//- minicore: result
+fn fallible() -> Result<(), ()> {
+    $0if true {
+        return Err(());
+    }
+    Ok(())$0
+}
+"#,
+            r#"
+fn fallible() -> Result<(), ()> {
+    fun_name()
+}
+
+fn $0fun_name() -> Result<(), ()> {
+    if true {
+        return Err(());
+    }
+    Ok(())
+}
+"#,
+        );
+    }
+
     #[test]
     fn non_tail_expr_of_tail_expr_loop() {
         check_assist(
@@ -5800,12 +5819,6 @@ fn $0fun_name() -> ControlFlow<()> {
             extract_function,
             r#"
 //- minicore: option, try
-impl<T> core::ops::Try for Option<T> {
-    type Output = T;
-    type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
 fn f() -> Option<()> {
     if true {
         let a = $0if true {
@@ -5820,12 +5833,6 @@ fn f() -> Option<()> {
 }
 "#,
             r#"
-impl<T> core::ops::Try for Option<T> {
-    type Output = T;
-    type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
 fn f() -> Option<()> {
     if true {
         let a = fun_name()?;;
@@ -5852,12 +5859,6 @@ fn $0fun_name() -> Option<()> {
             extract_function,
             r#"
 //- minicore: option, try
-impl<T> core::ops::Try for Option<T> {
-    type Output = T;
-    type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
 fn f() -> Option<()> {
     if true {
         $0{
@@ -5874,15 +5875,9 @@ fn f() -> Option<()> {
 }
 "#,
             r#"
-impl<T> core::ops::Try for Option<T> {
-    type Output = T;
-    type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
 fn f() -> Option<()> {
     if true {
-        fun_name()?
+        fun_name()
     } else {
         None
     }
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 31fc69562c9..bbac0a26ea4 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -95,6 +95,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
         let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else {
             continue;
         };
+
+        let field = make::ext::field_from_idents(["self", &field_name])?;
+
         acc.add_group(
             &GroupLabel("Generate delegate methods…".to_owned()),
             AssistId("generate_delegate_methods", AssistKind::Generate),
@@ -115,11 +118,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                     Some(list) => convert_param_list_to_arg_list(list),
                     None => make::arg_list([]),
                 };
-                let tail_expr = make::expr_method_call(
-                    make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
-                    make::name_ref(&name),
-                    arg_list,
-                );
+                let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list);
                 let ret_type = method_source.ret_type();
                 let is_async = method_source.async_token().is_some();
                 let is_const = method_source.const_token().is_some();
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs
index 747f70f9f6f..53ba144ba9e 100644
--- a/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -27,13 +27,19 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
     let cap = ctx.config.snippet_cap?;
     let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
     let target = nominal.syntax().text_range();
+    let derive_attr = nominal
+        .attrs()
+        .filter_map(|x| x.as_simple_call())
+        .filter(|(name, _arg)| name == "derive")
+        .map(|(_name, arg)| arg)
+        .next();
+
+    let delimiter = match &derive_attr {
+        None => None,
+        Some(tt) => Some(tt.right_delimiter_token()?),
+    };
+
     acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| {
-        let derive_attr = nominal
-            .attrs()
-            .filter_map(|x| x.as_simple_call())
-            .filter(|(name, _arg)| name == "derive")
-            .map(|(_name, arg)| arg)
-            .next();
         match derive_attr {
             None => {
                 let derive = make::attr_outer(make::meta_token_tree(
@@ -45,16 +51,23 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
                 let nominal = edit.make_mut(nominal);
                 nominal.add_attr(derive.clone());
 
+                let delimiter = derive
+                    .meta()
+                    .expect("make::attr_outer was expected to have Meta")
+                    .token_tree()
+                    .expect("failed to get token tree out of Meta")
+                    .r_paren_token()
+                    .expect("make::attr_outer was expected to have a R_PAREN");
+
+                edit.add_tabstop_before_token(cap, delimiter);
+            }
+            Some(_) => {
+                // Just move the cursor.
                 edit.add_tabstop_before_token(
                     cap,
-                    derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(),
+                    delimiter.expect("Right delim token could not be found."),
                 );
             }
-            Some(tt) => {
-                // Just move the cursor.
-                let tt = edit.make_mut(tt);
-                edit.add_tabstop_before_token(cap, tt.right_delimiter_token().unwrap());
-            }
         };
     })
 }
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index a403d5bc672..e2b82223289 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -39,14 +39,11 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
 
     let replacements =
         macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
-    if replacements.is_empty() {
-        return None;
-    }
 
     acc.add(
         AssistId("remove_dbg", AssistKind::Refactor),
         "Remove dbg!()",
-        replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range)).unwrap(),
+        replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
         |builder| {
             for (range, expr) in replacements {
                 if let Some(expr) = expr {
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs
index dd4839351fb..5fcab8c02b0 100644
--- a/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -67,7 +67,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
                 // This case maps to the situation where the * token is braced.
                 // In this case, the parent use tree's path is the one we should use to resolve the glob.
                 match u.syntax().ancestors().skip(1).find_map(ast::UseTree::cast) {
-                    Some(parent_u) if parent_u.path().is_some() => parent_u.path().unwrap(),
+                    Some(parent_u) if parent_u.path().is_some() => parent_u.path()?,
                     _ => return None,
                 }
             } else {
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 480cb77b4fd..f60ac150164 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -20,6 +20,7 @@ pub(crate) mod r#type;
 pub(crate) mod use_;
 pub(crate) mod vis;
 pub(crate) mod env_vars;
+pub(crate) mod extern_crate;
 
 use std::iter;
 
@@ -703,7 +704,9 @@ pub(super) fn complete_name_ref(
                         TypeLocation::TypeAscription(ascription) => {
                             r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription);
                         }
-                        TypeLocation::GenericArgList(_)
+                        TypeLocation::GenericArg { .. }
+                        | TypeLocation::AssocConstEq
+                        | TypeLocation::AssocTypeEq
                         | TypeLocation::TypeBound
                         | TypeLocation::ImplTarget
                         | TypeLocation::ImplTrait
@@ -737,6 +740,7 @@ pub(super) fn complete_name_ref(
                 }
             }
         }
+        NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx),
         NameRefKind::DotAccess(dot_access) => {
             flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
             dot::complete_dot(acc, ctx, dot_access);
diff --git a/crates/ide-completion/src/completions/extern_crate.rs b/crates/ide-completion/src/completions/extern_crate.rs
new file mode 100644
index 00000000000..0d0e143f5f6
--- /dev/null
+++ b/crates/ide-completion/src/completions/extern_crate.rs
@@ -0,0 +1,71 @@
+//! Completion for extern crates
+
+use hir::{HasAttrs, Name};
+use ide_db::SymbolKind;
+
+use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};
+
+use super::Completions;
+
+pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+    let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect();
+
+    for (name, module) in ctx.scope.extern_crates() {
+        if imported_extern_crates.contains(&name) {
+            continue;
+        }
+
+        let mut item = CompletionItem::new(
+            CompletionItemKind::SymbolKind(SymbolKind::Module),
+            ctx.source_range(),
+            name.to_smol_str(),
+        );
+        item.set_documentation(module.docs(ctx.db));
+
+        item.add_to(acc, ctx.db);
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::tests::completion_list_no_kw;
+
+    #[test]
+    fn can_complete_extern_crate() {
+        let case = r#"
+//- /lib.rs crate:other_crate_a
+// nothing here
+//- /other_crate_b.rs crate:other_crate_b
+pub mod good_mod{}
+//- /lib.rs crate:crate_c
+// nothing here
+//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a
+extern crate oth$0
+mod other_mod {}
+"#;
+
+        let completion_list = completion_list_no_kw(case);
+
+        assert_eq!("md other_crate_a\n".to_string(), completion_list);
+    }
+
+    #[test]
+    fn will_not_complete_existing_import() {
+        let case = r#"
+//- /lib.rs crate:other_crate_a
+// nothing here
+//- /lib.rs crate:crate_c
+// nothing here
+//- /lib.rs crate:other_crate_b
+//
+//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b
+extern crate other_crate_b;
+extern crate oth$0
+mod other_mod {}
+"#;
+
+        let completion_list = completion_list_no_kw(case);
+
+        assert_eq!("md other_crate_a\n".to_string(), completion_list);
+    }
+}
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index e4705475638..a30fd13b1d5 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -1,7 +1,7 @@
 //! Completion of names from the current scope in type position.
 
 use hir::{HirDisplay, ScopeDef};
-use syntax::{ast, AstNode, SyntaxKind};
+use syntax::{ast, AstNode};
 
 use crate::{
     context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@@ -20,16 +20,15 @@ pub(crate) fn complete_type_path(
     let scope_def_applicable = |def| {
         use hir::{GenericParam::*, ModuleDef::*};
         match def {
-            ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
+            ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(),
+            ScopeDef::Label(_) => false,
             // no values in type places
             ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
             // unless its a constant in a generic arg list position
             ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
-                matches!(location, TypeLocation::GenericArgList(_))
-            }
-            ScopeDef::ImplSelfType(_) => {
-                !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
+                location.complete_consts()
             }
+            ScopeDef::ImplSelfType(_) => location.complete_self_type(),
             // Don't suggest attribute macros and derives.
             ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
             // Type things are fine
@@ -38,12 +37,12 @@ pub(crate) fn complete_type_path(
             )
             | ScopeDef::AdtSelfType(_)
             | ScopeDef::Unknown
-            | ScopeDef::GenericParam(TypeParam(_)) => true,
+            | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(),
         }
     };
 
     let add_assoc_item = |acc: &mut Completions, item| match item {
-        hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
+        hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => {
             acc.add_const(ctx, ct)
         }
         hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
@@ -157,56 +156,30 @@ pub(crate) fn complete_type_path(
                     });
                     return;
                 }
-                TypeLocation::GenericArgList(Some(arg_list)) => {
-                    let in_assoc_type_arg = ctx
-                        .original_token
-                        .parent_ancestors()
-                        .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
+                TypeLocation::GenericArg {
+                    args: Some(arg_list), of_trait: Some(trait_), ..
+                } => {
+                    if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
+                        let arg_idx = arg_list
+                            .generic_args()
+                            .filter(|arg| {
+                                arg.syntax().text_range().end()
+                                    < ctx.original_token.text_range().start()
+                            })
+                            .count();
 
-                    if !in_assoc_type_arg {
-                        if let Some(path_seg) =
-                            arg_list.syntax().parent().and_then(ast::PathSegment::cast)
-                        {
-                            if path_seg
-                                .syntax()
-                                .ancestors()
-                                .find_map(ast::TypeBound::cast)
-                                .is_some()
-                            {
-                                if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
-                                    trait_,
-                                ))) = ctx.sema.resolve_path(&path_seg.parent_path())
-                                {
-                                    let arg_idx = arg_list
-                                        .generic_args()
-                                        .filter(|arg| {
-                                            arg.syntax().text_range().end()
-                                                < ctx.original_token.text_range().start()
-                                        })
-                                        .count();
-
-                                    let n_required_params =
-                                        trait_.type_or_const_param_count(ctx.sema.db, true);
-                                    if arg_idx >= n_required_params {
-                                        trait_
-                                            .items_with_supertraits(ctx.sema.db)
-                                            .into_iter()
-                                            .for_each(|it| {
-                                                if let hir::AssocItem::TypeAlias(alias) = it {
-                                                    cov_mark::hit!(
-                                                        complete_assoc_type_in_generics_list
-                                                    );
-                                                    acc.add_type_alias_with_eq(ctx, alias);
-                                                }
-                                            });
-
-                                        let n_params =
-                                            trait_.type_or_const_param_count(ctx.sema.db, false);
-                                        if arg_idx >= n_params {
-                                            return; // only show assoc types
-                                        }
-                                    }
+                        let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true);
+                        if arg_idx >= n_required_params {
+                            trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| {
+                                if let hir::AssocItem::TypeAlias(alias) = it {
+                                    cov_mark::hit!(complete_assoc_type_in_generics_list);
+                                    acc.add_type_alias_with_eq(ctx, alias);
                                 }
+                            });
+
+                            let n_params = trait_.type_or_const_param_count(ctx.sema.db, false);
+                            if arg_idx >= n_params {
+                                return; // only show assoc types
                             }
                         }
                     }
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 3cb65b2729a..0da7ba6d000 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -155,13 +155,63 @@ pub(crate) struct ExprCtx {
 pub(crate) enum TypeLocation {
     TupleField,
     TypeAscription(TypeAscriptionTarget),
-    GenericArgList(Option<ast::GenericArgList>),
+    /// Generic argument position e.g. `Foo<$0>`
+    GenericArg {
+        /// The generic argument list containing the generic arg
+        args: Option<ast::GenericArgList>,
+        /// `Some(trait_)` if `trait_` is being instantiated with `args`
+        of_trait: Option<hir::Trait>,
+        /// The generic parameter being filled in by the generic arg
+        corresponding_param: Option<ast::GenericParam>,
+    },
+    /// Associated type equality constraint e.g. `Foo<Bar = $0>`
+    AssocTypeEq,
+    /// Associated constant equality constraint e.g. `Foo<X = $0>`
+    AssocConstEq,
     TypeBound,
     ImplTarget,
     ImplTrait,
     Other,
 }
 
+impl TypeLocation {
+    pub(crate) fn complete_lifetimes(&self) -> bool {
+        matches!(
+            self,
+            TypeLocation::GenericArg {
+                corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
+                ..
+            }
+        )
+    }
+
+    pub(crate) fn complete_consts(&self) -> bool {
+        match self {
+            TypeLocation::GenericArg {
+                corresponding_param: Some(ast::GenericParam::ConstParam(_)),
+                ..
+            } => true,
+            TypeLocation::AssocConstEq => true,
+            _ => false,
+        }
+    }
+
+    pub(crate) fn complete_types(&self) -> bool {
+        match self {
+            TypeLocation::GenericArg { corresponding_param: Some(param), .. } => {
+                matches!(param, ast::GenericParam::TypeParam(_))
+            }
+            TypeLocation::AssocConstEq => false,
+            TypeLocation::AssocTypeEq => true,
+            _ => true,
+        }
+    }
+
+    pub(crate) fn complete_self_type(&self) -> bool {
+        self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum TypeAscriptionTarget {
     Let(Option<ast::Pat>),
@@ -301,6 +351,7 @@ pub(super) enum NameRefKind {
         expr: ast::RecordExpr,
     },
     Pattern(PatternContext),
+    ExternCrate,
 }
 
 /// The identifier we are currently completing.
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 3ea50659030..1e6b2f319aa 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1,11 +1,11 @@
 //! Module responsible for analyzing the code surrounding the cursor for completion.
 use std::iter;
 
-use hir::{Semantics, Type, TypeInfo, Variant};
+use hir::{HasSource, Semantics, Type, TypeInfo, Variant};
 use ide_db::{active_parameter::ActiveParameter, RootDatabase};
 use syntax::{
     algo::{find_node_at_offset, non_trivia_sibling},
-    ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef},
+    ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef},
     match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
     SyntaxToken, TextRange, TextSize, T,
 };
@@ -624,6 +624,10 @@ fn classify_name_ref(
                 });
                 return Some(make_res(kind));
             },
+            ast::ExternCrate(_) => {
+                let kind = NameRefKind::ExternCrate;
+                return Some(make_res(kind));
+            },
             ast::MethodCallExpr(method) => {
                 let receiver = find_opt_node_in_file(original_file, method.receiver());
                 let kind = NameRefKind::DotAccess(DotAccess {
@@ -719,6 +723,136 @@ fn classify_name_ref(
         None
     };
 
+    let generic_arg_location = |arg: ast::GenericArg| {
+        let mut override_location = None;
+        let location = find_opt_node_in_file_compensated(
+            sema,
+            original_file,
+            arg.syntax().parent().and_then(ast::GenericArgList::cast),
+        )
+        .map(|args| {
+            let mut in_trait = None;
+            let param = (|| {
+                let parent = args.syntax().parent()?;
+                let params = match_ast! {
+                    match parent {
+                        ast::PathSegment(segment) => {
+                            match sema.resolve_path(&segment.parent_path().top_path())? {
+                                hir::PathResolution::Def(def) => match def {
+                                    hir::ModuleDef::Function(func) => {
+                                        func.source(sema.db)?.value.generic_param_list()
+                                    }
+                                    hir::ModuleDef::Adt(adt) => {
+                                        adt.source(sema.db)?.value.generic_param_list()
+                                    }
+                                    hir::ModuleDef::Variant(variant) => {
+                                        variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list()
+                                    }
+                                    hir::ModuleDef::Trait(trait_) => {
+                                        if let ast::GenericArg::AssocTypeArg(arg) = &arg {
+                                            let arg_name = arg.name_ref()?;
+                                            let arg_name = arg_name.text();
+                                            for item in trait_.items_with_supertraits(sema.db) {
+                                                match item {
+                                                    hir::AssocItem::TypeAlias(assoc_ty) => {
+                                                        if assoc_ty.name(sema.db).as_str()? == arg_name {
+                                                            override_location = Some(TypeLocation::AssocTypeEq);
+                                                            return None;
+                                                        }
+                                                    },
+                                                    hir::AssocItem::Const(const_) => {
+                                                        if const_.name(sema.db)?.as_str()? == arg_name {
+                                                            override_location =  Some(TypeLocation::AssocConstEq);
+                                                            return None;
+                                                        }
+                                                    },
+                                                    _ => (),
+                                                }
+                                            }
+                                            return None;
+                                        } else {
+                                            in_trait = Some(trait_);
+                                            trait_.source(sema.db)?.value.generic_param_list()
+                                        }
+                                    }
+                                    hir::ModuleDef::TraitAlias(trait_) => {
+                                        trait_.source(sema.db)?.value.generic_param_list()
+                                    }
+                                    hir::ModuleDef::TypeAlias(ty_) => {
+                                        ty_.source(sema.db)?.value.generic_param_list()
+                                    }
+                                    _ => None,
+                                },
+                                _ => None,
+                            }
+                        },
+                        ast::MethodCallExpr(call) => {
+                            let func = sema.resolve_method_call(&call)?;
+                            func.source(sema.db)?.value.generic_param_list()
+                        },
+                        ast::AssocTypeArg(arg) => {
+                            let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?;
+                            match sema.resolve_path(&trait_.parent_path().top_path())? {
+                                hir::PathResolution::Def(def) => match def {
+                                    hir::ModuleDef::Trait(trait_) => {
+                                        let arg_name = arg.name_ref()?;
+                                        let arg_name = arg_name.text();
+                                        let trait_items = trait_.items_with_supertraits(sema.db);
+                                        let assoc_ty = trait_items.iter().find_map(|item| match item {
+                                            hir::AssocItem::TypeAlias(assoc_ty) => {
+                                                (assoc_ty.name(sema.db).as_str()? == arg_name)
+                                                    .then_some(assoc_ty)
+                                            },
+                                            _ => None,
+                                        })?;
+                                        assoc_ty.source(sema.db)?.value.generic_param_list()
+                                    }
+                                    _ => None,
+                                },
+                                _ => None,
+                            }
+                        },
+                        _ => None,
+                    }
+                }?;
+                // Determine the index of the argument in the `GenericArgList` and match it with
+                // the corresponding parameter in the `GenericParamList`. Since lifetime parameters
+                // are often omitted, ignore them for the purposes of matching the argument with
+                // its parameter unless a lifetime argument is provided explicitly. That is, for
+                // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`.
+                // FIXME: This operates on the syntax tree and will produce incorrect results when
+                // generic parameters are disabled by `#[cfg]` directives. It should operate on the
+                // HIR, but the functionality necessary to do so is not exposed at the moment.
+                let mut explicit_lifetime_arg = false;
+                let arg_idx = arg
+                    .syntax()
+                    .siblings(Direction::Prev)
+                    // Skip the node itself
+                    .skip(1)
+                    .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true })
+                    .count();
+                let param_idx = if explicit_lifetime_arg {
+                    arg_idx
+                } else {
+                    // Lifetimes parameters always precede type and generic parameters,
+                    // so offset the argument index by the total number of lifetime params
+                    arg_idx + params.lifetime_params().count()
+                };
+                params.generic_params().nth(param_idx)
+            })();
+            (args, in_trait, param)
+        });
+        let (arg_list, of_trait, corresponding_param) = match location {
+            Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param),
+            _ => (None, None, None),
+        };
+        override_location.unwrap_or(TypeLocation::GenericArg {
+            args: arg_list,
+            of_trait,
+            corresponding_param,
+        })
+    };
+
     let type_location = |node: &SyntaxNode| {
         let parent = node.parent()?;
         let res = match_ast! {
@@ -774,9 +908,12 @@ fn classify_name_ref(
                 ast::TypeBound(_) => TypeLocation::TypeBound,
                 // is this case needed?
                 ast::TypeBoundList(_) => TypeLocation::TypeBound,
-                ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
+                ast::GenericArg(it) => generic_arg_location(it),
                 // is this case needed?
-                ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
+                ast::GenericArgList(it) => {
+                    let args = find_opt_node_in_file_compensated(sema, original_file, Some(it));
+                    TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None }
+                },
                 ast::TupleField(_) => TypeLocation::TupleField,
                 _ => return None,
             }
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 8c038c0fbaa..4cdfd546f6a 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1286,3 +1286,57 @@ macro_rules! println {
         expect![""],
     )
 }
+
+#[test]
+fn no_completions_for_external_doc_hidden_in_path() {
+    check(
+        r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+    Span$0
+}
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub mod bridge {
+    pub mod server {
+        pub trait Span
+    }
+}
+pub mod bridge2 {
+    #[doc(hidden)]
+    pub mod server2 {
+        pub trait Span
+    }
+}
+"#,
+        expect![""],
+    );
+    // unless re-exported
+    check(
+        r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+    Span$0
+}
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub mod bridge {
+    pub mod server {
+        pub trait Span
+    }
+}
+pub use bridge::server::Span;
+pub mod bridge2 {
+    #[doc(hidden)]
+    pub mod server2 {
+        pub trait Span2
+    }
+}
+pub use bridge2::server2::Span2;
+"#,
+        expect![[r#"
+            tt Span (use dep::Span)
+            tt Span2 (use dep::Span2)
+        "#]],
+    );
+}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 8cb1ff4a125..d518dd76410 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -384,10 +384,8 @@ trait Trait2<T>: Trait1 {
 fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
 "#,
         expect![[r#"
-            ct CONST
-            cp CONST_PARAM
             en Enum
-            ma makro!(…)   macro_rules! makro
+            ma makro!(…) macro_rules! makro
             md module
             st Record
             st Tuple
@@ -404,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
     );
     check(
         r#"
-trait Trait2 {
+trait Trait2<T> {
     type Foo;
 }
 
 fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
     "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -437,7 +434,6 @@ trait Tr<T> {
 impl Tr<$0
     "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -485,7 +481,6 @@ trait MyTrait<T, U> {
 fn f(t: impl MyTrait<u$0
 "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -511,7 +506,6 @@ trait MyTrait<T, U> {
 fn f(t: impl MyTrait<u8, u$0
 "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -555,7 +549,6 @@ trait MyTrait<T, U = u8> {
 fn f(t: impl MyTrait<u$0
 "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -581,7 +574,6 @@ trait MyTrait<T, U = u8> {
 fn f(t: impl MyTrait<u8, u$0
 "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…)             macro_rules! makro
             md module
@@ -627,7 +619,6 @@ trait MyTrait {
 fn f(t: impl MyTrait<Item1 = $0
 "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -653,7 +644,6 @@ trait MyTrait {
 fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
 "#,
         expect![[r#"
-            ct CONST
             en Enum
             ma makro!(…) macro_rules! makro
             md module
@@ -668,6 +658,22 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
             kw self::
         "#]],
     );
+
+    check(
+        r#"
+trait MyTrait {
+    const C: usize;
+};
+
+fn f(t: impl MyTrait<C = $0
+"#,
+        expect![[r#"
+            ct CONST
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
+    );
 }
 
 #[test]
@@ -719,3 +725,267 @@ pub struct S;
         "#]],
     )
 }
+
+#[test]
+fn completes_const_and_type_generics_separately() {
+    // Function generic params
+    check(
+        r#"
+    struct Foo;
+    const X: usize = 0;
+    fn foo<T, const N: usize>() {}
+    fn main() {
+        foo::<F$0, _>();
+    }
+            "#,
+        expect![[r#"
+                en Enum
+                ma makro!(…) macro_rules! makro
+                md module
+                st Foo
+                st Record
+                st Tuple
+                st Unit
+                tt Trait
+                un Union
+                bt u32
+                kw crate::
+                kw self::
+            "#]],
+    );
+    // FIXME: This should probably also suggest completions for types, at least those that have
+    // associated constants usable in this position. For example, a user could be typing
+    // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position.
+    check(
+        r#"
+    struct Foo;
+    const X: usize = 0;
+    fn foo<T, const N: usize>() {}
+    fn main() {
+        foo::<_, $0>();
+    }
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Method generic params
+    check(
+        r#"
+    const X: usize = 0;
+    struct Foo;
+    impl Foo { fn bar<const N: usize, T>(self) {} }
+    fn main() {
+        Foo.bar::<_, $0>();
+    }
+            "#,
+        expect![[r#"
+                en Enum
+                ma makro!(…) macro_rules! makro
+                md module
+                st Foo
+                st Record
+                st Tuple
+                st Unit
+                tt Trait
+                un Union
+                bt u32
+                kw crate::
+                kw self::
+            "#]],
+    );
+    check(
+        r#"
+    const X: usize = 0;
+    struct Foo;
+    impl Foo { fn bar<const N: usize, T>(self) {} }
+    fn main() {
+        Foo.bar::<X$0, _>();
+    }
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Associated type generic params
+    check(
+        r#"
+    const X: usize = 0;
+    struct Foo;
+    trait Bar {
+        type Baz<T, const X: usize>;
+    }
+    fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
+            "#,
+        expect![[r#"
+                en Enum
+                ma makro!(…) macro_rules! makro
+                md module
+                st Foo
+                st Record
+                st Tuple
+                st Unit
+                tt Bar
+                tt Trait
+                un Union
+                bt u32
+                kw crate::
+                kw self::
+            "#]],
+    );
+    check(
+        r#"
+    const X: usize = 0;
+    struct Foo;
+    trait Bar {
+        type Baz<T, const X: usize>;
+    }
+    fn foo<T: Bar<Baz<(), $0> = ()>>() {}
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Type generic params
+    check(
+        r#"
+    const X: usize = 0;
+    struct Foo<T, const N: usize>(T);
+    fn main() {
+        let _: Foo::<_, $0> = Foo(());
+    }
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Type alias generic params
+    check(
+        r#"
+    const X: usize = 0;
+    struct Foo<T, const N: usize>(T);
+    type Bar<const X: usize, U> = Foo<U, X>;
+    fn main() {
+        let _: Bar::<X$0, _> = Bar(());
+    }
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Enum variant params
+    check(
+        r#"
+    const X: usize = 0;
+    enum Foo<T, const N: usize> { A(T), B }
+    fn main() {
+        Foo::B::<(), $0>;
+    }
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Trait params
+    check(
+        r#"
+    const X: usize = 0;
+    trait Foo<T, const N: usize> {}
+    impl Foo<(), $0> for () {}
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Trait alias params
+    check(
+        r#"
+    #![feature(trait_alias)]
+    const X: usize = 0;
+    trait Foo<T, const N: usize> {}
+    trait Bar<const M: usize, U> = Foo<U, M>;
+    fn foo<T: Bar<X$0, ()>>() {}
+            "#,
+        expect![[r#"
+                ct CONST
+                ct X
+                ma makro!(…) macro_rules! makro
+                kw crate::
+                kw self::
+            "#]],
+    );
+
+    // Omitted lifetime params
+    check(
+        r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<F$0, _>; }
+        "#,
+        expect![[r#"
+            ct CONST
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
+    );
+    // Explicit lifetime params
+    check(
+        r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, 'static, F$0, _>; }
+        "#,
+        expect![[r#"
+            ct CONST
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
+    );
+    check(
+        r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, F$0, _, _>; }
+        "#,
+        expect![[r#"
+            lt 'a
+            ma makro!(…) macro_rules! makro
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 5e4562d9c58..4ce80532e8e 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -7,7 +7,7 @@
 
 use arrayvec::ArrayVec;
 use hir::{
-    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper,
+    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef,
     ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
     Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
     TypeAlias, Variant, Visibility,
@@ -649,3 +649,13 @@ impl From<ModuleDef> for Definition {
         }
     }
 }
+
+impl From<DocLinkDef> for Definition {
+    fn from(def: DocLinkDef) -> Self {
+        match def {
+            DocLinkDef::ModuleDef(it) => it.into(),
+            DocLinkDef::Field(it) => it.into(),
+            DocLinkDef::SelfType(it) => it.into(),
+        }
+    }
+}
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs
index 1eb8f00020b..330af442f75 100644
--- a/crates/ide-db/src/helpers.rs
+++ b/crates/ide-db/src/helpers.rs
@@ -117,7 +117,7 @@ pub fn get_definition(
     sema: &Semantics<'_, RootDatabase>,
     token: SyntaxToken,
 ) -> Option<Definition> {
-    for token in sema.descend_into_macros(token) {
+    for token in sema.descend_into_macros(token, 0.into()) {
         let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
         if let Some(&[x]) = def.as_deref() {
             return Some(x);
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index e52dc356775..e475c5cd66b 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -6,7 +6,7 @@ use hir::{
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use syntax::{
-    ast::{self, HasName},
+    ast::{self, make, HasName},
     utils::path_to_string_stripping_turbo_fish,
     AstNode, SyntaxNode,
 };
@@ -607,7 +607,7 @@ impl ImportCandidate {
     fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
         if sema
             .scope(name.syntax())?
-            .speculative_resolve(&ast::make::ext::ident_path(&name.text()))
+            .speculative_resolve(&make::ext::ident_path(&name.text()))
             .is_some()
         {
             return None;
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index f27ed485d81..ac3511ba47b 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -94,18 +94,21 @@ impl fmt::Debug for RootDatabase {
 }
 
 impl Upcast<dyn ExpandDatabase> for RootDatabase {
+    #[inline]
     fn upcast(&self) -> &(dyn ExpandDatabase + 'static) {
         &*self
     }
 }
 
 impl Upcast<dyn DefDatabase> for RootDatabase {
+    #[inline]
     fn upcast(&self) -> &(dyn DefDatabase + 'static) {
         &*self
     }
 }
 
 impl Upcast<dyn HirDatabase> for RootDatabase {
+    #[inline]
     fn upcast(&self) -> &(dyn HirDatabase + 'static) {
         &*self
     }
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 1d0cb426a57..fb75b5b4584 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -5,7 +5,7 @@ use either::Either;
 use hir::{AsAssocItem, HirDisplay, SemanticsScope};
 use rustc_hash::FxHashMap;
 use syntax::{
-    ast::{self, AstNode},
+    ast::{self, make, AstNode},
     ted, SyntaxNode,
 };
 
@@ -21,6 +21,7 @@ enum TypeOrConst {
 }
 
 type LifetimeName = String;
+type DefaultedParam = Either<hir::TypeParam, hir::ConstParam>;
 
 /// `PathTransform` substitutes path in SyntaxNodes in bulk.
 ///
@@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> {
         };
         let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
         let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
-        let mut default_types: Vec<hir::TypeParam> = Default::default();
+        let mut defaulted_params: Vec<DefaultedParam> = Default::default();
         self.generic_def
             .into_iter()
             .flat_map(|it| it.type_params(db))
@@ -138,8 +139,8 @@ impl<'a> PathTransform<'a> {
                         if let Some(default) =
                             &default.display_source_code(db, source_module.into(), false).ok()
                         {
-                            type_substs.insert(k, ast::make::ty(default).clone_for_update());
-                            default_types.push(k);
+                            type_substs.insert(k, make::ty(default).clone_for_update());
+                            defaulted_params.push(Either::Left(k));
                         }
                     }
                 }
@@ -155,11 +156,19 @@ impl<'a> PathTransform<'a> {
                         // is a standalone statement or a part of another expresson)
                         // and sometimes require slight modifications; see
                         // https://doc.rust-lang.org/reference/statements.html#expression-statements
+                        // (default values in curly brackets can cause the same problem)
                         const_substs.insert(k, expr.syntax().clone());
                     }
                 }
-                (Either::Left(_), None) => (), // FIXME: get default const value
-                _ => (),                       // ignore mismatching params
+                (Either::Left(k), None) => {
+                    if let Some(default) = k.default(db) {
+                        if let Some(default) = default.expr() {
+                            const_substs.insert(k, default.syntax().clone_for_update());
+                            defaulted_params.push(Either::Right(k));
+                        }
+                    }
+                }
+                _ => (), // ignore mismatching params
             });
         let lifetime_substs: FxHashMap<_, _> = self
             .generic_def
@@ -175,7 +184,7 @@ impl<'a> PathTransform<'a> {
             target_module,
             source_scope: self.source_scope,
         };
-        ctx.transform_default_type_substs(default_types);
+        ctx.transform_default_values(defaulted_params);
         ctx
     }
 }
@@ -212,13 +221,19 @@ impl Ctx<'_> {
         });
     }
 
-    fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
-        for k in default_types {
-            let v = self.type_substs.get(&k).unwrap();
+    fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) {
+        // By now the default values are simply copied from where they are declared
+        // and should be transformed. As any value is allowed to refer to previous
+        // generic (both type and const) parameters, they should be all iterated left-to-right.
+        for param in defaulted_params {
+            let value = match param {
+                Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(),
+                Either::Right(k) => self.const_substs.get(&k).unwrap(),
+            };
             // `transform_path` may update a node's parent and that would break the
             // tree traversal. Thus all paths in the tree are collected into a vec
             // so that such operation is safe.
-            let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+            let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
             for path in paths {
                 self.transform_path(path);
             }
@@ -263,15 +278,14 @@ impl Ctx<'_> {
                                 hir::ModuleDef::Trait(trait_ref),
                                 false,
                             )?;
-                            match ast::make::ty_path(mod_path_to_ast(&found_path)) {
+                            match make::ty_path(mod_path_to_ast(&found_path)) {
                                 ast::Type::PathType(path_ty) => Some(path_ty),
                                 _ => None,
                             }
                         });
 
-                        let segment = ast::make::path_segment_ty(subst.clone(), trait_ref);
-                        let qualified =
-                            ast::make::path_from_segments(std::iter::once(segment), false);
+                        let segment = make::path_segment_ty(subst.clone(), trait_ref);
+                        let qualified = make::path_from_segments(std::iter::once(segment), false);
                         ted::replace(path.syntax(), qualified.clone_for_update().syntax());
                     } else if let Some(path_ty) = ast::PathType::cast(parent) {
                         ted::replace(
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index d5abd099126..7e00d368652 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -456,14 +456,14 @@ impl<'a> FindUsages<'a> {
                     it.text().trim_start_matches("r#") == name
                 })
                 .into_iter()
-                .flat_map(|token| {
+                .flat_map(move |token| {
                     // FIXME: There should be optimization potential here
                     // Currently we try to descend everything we find which
                     // means we call `Semantics::descend_into_macros` on
                     // every textual hit. That function is notoriously
                     // expensive even for things that do not get down mapped
                     // into macros.
-                    sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent())
+                    sema.descend_into_macros(token, offset).into_iter().filter_map(|it| it.parent())
                 })
         };
 
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index b54c43b296b..f699f999baf 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -323,6 +323,8 @@ impl Query {
                             hir::ModuleDef::Adt(..)
                                 | hir::ModuleDef::TypeAlias(..)
                                 | hir::ModuleDef::BuiltinType(..)
+                                | hir::ModuleDef::TraitAlias(..)
+                                | hir::ModuleDef::Trait(..)
                         )
                     {
                         continue;
@@ -417,9 +419,16 @@ const CONST_WITH_INNER: () = {
 
 mod b_mod;
 
+
+use define_struct as really_define_struct;
+use Macro as ItemLikeMacro;
+use Macro as Trait; // overlay namespaces
 //- /b_mod.rs
 struct StructInModB;
-        "#,
+use super::Macro as SuperItemLikeMacro;
+use crate::b_mod::StructInModB as ThisStruct;
+use crate::Trait as IsThisJustATrait;
+"#,
         );
 
         let symbols: Vec<_> = Crate::from(db.test_crate())
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 1a00e29384e..87ad5844c64 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -118,6 +118,35 @@
                 container_name: None,
                 is_alias: false,
             },
+            FileSymbol {
+                name: "ItemLikeMacro",
+                def: Macro(
+                    Macro {
+                        id: Macro2Id(
+                            Macro2Id(
+                                0,
+                            ),
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 654..676,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 663..676,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
             FileSymbol {
                 name: "Macro",
                 def: Macro(
@@ -352,6 +381,35 @@
                 container_name: None,
                 is_alias: false,
             },
+            FileSymbol {
+                name: "Trait",
+                def: Macro(
+                    Macro {
+                        id: Macro2Id(
+                            Macro2Id(
+                                0,
+                            ),
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 682..696,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 691..696,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
             FileSymbol {
                 name: "Union",
                 def: Adt(
@@ -551,6 +609,35 @@
                 container_name: None,
                 is_alias: false,
             },
+            FileSymbol {
+                name: "really_define_struct",
+                def: Macro(
+                    Macro {
+                        id: MacroRulesId(
+                            MacroRulesId(
+                                1,
+                            ),
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 611..648,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 628..648,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
             FileSymbol {
                 name: "trait_fn",
                 def: Function(
@@ -631,6 +718,35 @@
             },
         },
         [
+            FileSymbol {
+                name: "IsThisJustATrait",
+                def: Macro(
+                    Macro {
+                        id: Macro2Id(
+                            Macro2Id(
+                                0,
+                            ),
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            1,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 111..143,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 127..143,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
             FileSymbol {
                 name: "StructInModB",
                 def: Adt(
@@ -660,6 +776,93 @@
                 container_name: None,
                 is_alias: false,
             },
+            FileSymbol {
+                name: "SuperItemLikeMacro",
+                def: Macro(
+                    Macro {
+                        id: Macro2Id(
+                            Macro2Id(
+                                0,
+                            ),
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            1,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 25..59,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 41..59,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
+            FileSymbol {
+                name: "ThisStruct",
+                def: Adt(
+                    Struct(
+                        Struct {
+                            id: StructId(
+                                3,
+                            ),
+                        },
+                    ),
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            1,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 65..105,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 95..105,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
+            FileSymbol {
+                name: "ThisStruct",
+                def: Adt(
+                    Struct(
+                        Struct {
+                            id: StructId(
+                                3,
+                            ),
+                        },
+                    ),
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        FileId(
+                            1,
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: USE_TREE,
+                        range: 65..105,
+                    },
+                    name_ptr: SyntaxNodePtr {
+                        kind: NAME,
+                        range: 95..105,
+                    },
+                },
+                container_name: None,
+                is_alias: false,
+            },
         ],
     ),
 ]
diff --git a/crates/ide-db/src/use_trivial_constructor.rs b/crates/ide-db/src/use_trivial_constructor.rs
index f96ea29ae2f..a915391ad90 100644
--- a/crates/ide-db/src/use_trivial_constructor.rs
+++ b/crates/ide-db/src/use_trivial_constructor.rs
@@ -1,31 +1,29 @@
 //! Functionality for generating trivial constructors
 
 use hir::StructKind;
-use syntax::ast;
+use syntax::ast::{make, Expr, Path};
 
 /// given a type return the trivial constructor (if one exists)
 pub fn use_trivial_constructor(
     db: &crate::RootDatabase,
-    path: ast::Path,
+    path: Path,
     ty: &hir::Type,
-) -> Option<ast::Expr> {
+) -> Option<Expr> {
     match ty.as_adt() {
         Some(hir::Adt::Enum(x)) => {
             if let &[variant] = &*x.variants(db) {
                 if variant.kind(db) == hir::StructKind::Unit {
-                    let path = ast::make::path_qualified(
+                    let path = make::path_qualified(
                         path,
-                        syntax::ast::make::path_segment(ast::make::name_ref(
-                            &variant.name(db).to_smol_str(),
-                        )),
+                        make::path_segment(make::name_ref(&variant.name(db).to_smol_str())),
                     );
 
-                    return Some(syntax::ast::make::expr_path(path));
+                    return Some(make::expr_path(path));
                 }
             }
         }
         Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
-            return Some(syntax::ast::make::expr_path(path));
+            return Some(make::expr_path(path));
         }
         _ => {}
     }
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index a8e88369088..60fcbbbd397 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -560,8 +560,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                         placeholder_value.autoref_kind = self
                             .sema
                             .resolve_method_call_as_callable(code)
-                            .and_then(|callable| callable.receiver_param(self.sema.db))
-                            .map(|(self_param, _)| self_param.kind())
+                            .and_then(|callable| {
+                                let (self_param, _) = callable.receiver_param(self.sema.db)?;
+                                Some(self_param.source(self.sema.db)?.value.kind())
+                            })
                             .unwrap_or(ast::SelfParamKind::Owned);
                     }
                 }
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index dd1d0d75c63..f834f2ce592 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -74,18 +74,20 @@ pub(crate) fn incoming_calls(
     Some(calls.into_items())
 }
 
-pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
+pub(crate) fn outgoing_calls(
+    db: &RootDatabase,
+    FilePosition { file_id, offset }: FilePosition,
+) -> Option<Vec<CallItem>> {
     let sema = Semantics::new(db);
-    let file_id = position.file_id;
     let file = sema.parse(file_id);
     let file = file.syntax();
-    let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+    let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT => 1,
         _ => 0,
     })?;
     let mut calls = CallLocations::default();
 
-    sema.descend_into_macros(token)
+    sema.descend_into_macros(token, offset)
         .into_iter()
         .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast))
         .filter_map(|item| match item {
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index d240127f376..901f7a2a79a 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -131,19 +131,19 @@ pub(crate) fn remove_links(markdown: &str) -> String {
 // |===
 pub(crate) fn external_docs(
     db: &RootDatabase,
-    position: &FilePosition,
+    FilePosition { file_id, offset }: FilePosition,
     target_dir: Option<&OsStr>,
     sysroot: Option<&OsStr>,
 ) -> Option<DocumentationLinks> {
     let sema = &Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
-    let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+    let file = sema.parse(file_id).syntax().clone();
+    let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT | INT_NUMBER | T![self] => 3,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
         _ => 1,
     })?;
-    let token = sema.descend_into_macros_single(token);
+    let token = sema.descend_into_macros_single(token, offset);
 
     let node = token.parent()?;
     let definition = match_ast! {
@@ -285,7 +285,7 @@ impl DocCommentToken {
         let original_start = doc_token.text_range().start();
         let relative_comment_offset = offset - original_start - prefix_len;
 
-        sema.descend_into_macros(doc_token).into_iter().find_map(|t| {
+        sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| {
             let (node, descended_prefix_len) = match_ast! {
                 match t {
                     ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?),
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index 05a64b33bfd..8036c77072b 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -517,6 +517,62 @@ fn function();
     )
 }
 
+#[test]
+fn doc_links_field() {
+    check_doc_links(
+        r#"
+/// [`S::f`]
+/// [`S2::f`]
+/// [`T::0`]
+/// [`U::a`]
+/// [`E::A::f`]
+/// [`E::B::0`]
+struct S$0 {
+    f: i32,
+  //^ S::f
+  //^ S2::f
+}
+type S2 = S;
+struct T(i32);
+       //^^^ T::0
+union U {
+    a: i32,
+  //^ U::a
+}
+enum E {
+    A { f: i32 },
+      //^ E::A::f
+    B(i32),
+    //^^^ E::B::0
+}
+"#,
+    );
+}
+
+#[test]
+fn doc_links_field_via_self() {
+    check_doc_links(
+        r#"
+/// [`Self::f`]
+struct S$0 {
+    f: i32,
+  //^ Self::f
+}
+"#,
+    );
+}
+
+#[test]
+fn doc_links_tuple_field_via_self() {
+    check_doc_links(
+        r#"
+/// [`Self::0`]
+struct S$0(i32);
+       //^^^ Self::0
+"#,
+    );
+}
+
 #[test]
 fn rewrite_html_root_url() {
     check_rewrite(
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index d6bbf2bf794..119a9c7c3f4 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -40,28 +40,33 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
     // struct Bar;
     // ```
 
-    let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| {
-        let hir_file = sema.hir_file_for(&descended.parent()?);
-        if !hir_file.is_derive_attr_pseudo_expansion(db) {
-            return None;
-        }
+    let derive =
+        sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| {
+            let hir_file = sema.hir_file_for(&descended.parent()?);
+            if !hir_file.is_derive_attr_pseudo_expansion(db) {
+                return None;
+            }
 
-        let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
-        // up map out of the #[derive] expansion
-        let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
-        let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
-        let expansions = sema.expand_derive_macro(&attr)?;
-        let idx = attr
-            .token_tree()?
-            .token_trees_and_tokens()
-            .filter_map(NodeOrToken::into_token)
-            .take_while(|it| it != &token)
-            .filter(|it| it.kind() == T![,])
-            .count();
-        let expansion =
-            format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?);
-        Some(ExpandedMacro { name, expansion })
-    });
+            let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
+            // up map out of the #[derive] expansion
+            let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
+            let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+            let expansions = sema.expand_derive_macro(&attr)?;
+            let idx = attr
+                .token_tree()?
+                .token_trees_and_tokens()
+                .filter_map(NodeOrToken::into_token)
+                .take_while(|it| it != &token)
+                .filter(|it| it.kind() == T![,])
+                .count();
+            let expansion = format(
+                db,
+                SyntaxKind::MACRO_ITEMS,
+                position.file_id,
+                expansions.get(idx).cloned()?,
+            );
+            Some(ExpandedMacro { name, expansion })
+        });
 
     if derive.is_some() {
         return derive;
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index f9061822244..3d89599c583 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -17,8 +17,6 @@ use crate::FileRange;
 // Extends or shrinks the current selection to the encompassing syntactic construct
 // (expression, statement, item, module, etc). It works with multiple cursors.
 //
-// This is a standard LSP feature and not a protocol extension.
-//
 // |===
 // | Editor  | Shortcut
 //
@@ -142,8 +140,10 @@ fn extend_tokens_from_range(
 
     // compute original mapped token range
     let extended = {
-        let fst_expanded = sema.descend_into_macros_single(first_token.clone());
-        let lst_expanded = sema.descend_into_macros_single(last_token.clone());
+        let fst_expanded =
+            sema.descend_into_macros_single(first_token.clone(), original_range.start());
+        let lst_expanded =
+            sema.descend_into_macros_single(last_token.clone(), original_range.end());
         let mut lca =
             algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?;
         lca = shallowest_node(&lca);
@@ -154,13 +154,16 @@ fn extend_tokens_from_range(
     };
 
     // Compute parent node range
-    let validate = |token: &SyntaxToken| -> bool {
-        let expanded = sema.descend_into_macros_single(token.clone());
-        let parent = match expanded.parent() {
-            Some(it) => it,
-            None => return false,
-        };
-        algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended)
+    let validate = |offset: TextSize| {
+        let extended = &extended;
+        move |token: &SyntaxToken| -> bool {
+            let expanded = sema.descend_into_macros_single(token.clone(), offset);
+            let parent = match expanded.parent() {
+                Some(it) => it,
+                None => return false,
+            };
+            algo::least_common_ancestor(extended, &parent).as_ref() == Some(extended)
+        }
     };
 
     // Find the first and last text range under expanded parent
@@ -168,14 +171,14 @@ fn extend_tokens_from_range(
         let token = token.prev_token()?;
         skip_trivia_token(token, Direction::Prev)
     })
-    .take_while(validate)
+    .take_while(validate(original_range.start()))
     .last()?;
 
     let last = successors(Some(last_token), |token| {
         let token = token.next_token()?;
         skip_trivia_token(token, Direction::Next)
     })
-    .take_while(validate)
+    .take_while(validate(original_range.end()))
     .last()?;
 
     let range = first.text_range().cover(last.text_range());
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index c39c696cfd9..7e0fab42608 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -20,16 +20,16 @@ use crate::{
 // - fields in patterns will navigate to the field declaration of the struct, union or variant
 pub(crate) fn goto_declaration(
     db: &RootDatabase,
-    position: FilePosition,
+    position @ FilePosition { file_id, offset }: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
     let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
+    let file = sema.parse(file_id).syntax().clone();
     let original_token = file
-        .token_at_offset(position.offset)
+        .token_at_offset(offset)
         .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
     let range = original_token.text_range();
     let info: Vec<NavigationTarget> = sema
-        .descend_into_macros(original_token)
+        .descend_into_macros(original_token, offset)
         .iter()
         .filter_map(|token| {
             let parent = token.parent()?;
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 21471ab2a03..e09b9f39148 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -29,45 +29,39 @@ use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
 // image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
 pub(crate) fn goto_definition(
     db: &RootDatabase,
-    position: FilePosition,
+    FilePosition { file_id, offset }: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
     let sema = &Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
-    let original_token =
-        pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
-            IDENT
-            | INT_NUMBER
-            | LIFETIME_IDENT
-            | T![self]
-            | T![super]
-            | T![crate]
-            | T![Self]
-            | COMMENT => 4,
-            // index and prefix ops
-            T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
-            kind if kind.is_keyword() => 2,
-            T!['('] | T![')'] => 2,
-            kind if kind.is_trivia() => 0,
-            _ => 1,
-        })?;
+    let file = sema.parse(file_id).syntax().clone();
+    let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+        IDENT
+        | INT_NUMBER
+        | LIFETIME_IDENT
+        | T![self]
+        | T![super]
+        | T![crate]
+        | T![Self]
+        | COMMENT => 4,
+        // index and prefix ops
+        T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+        kind if kind.is_keyword() => 2,
+        T!['('] | T![')'] => 2,
+        kind if kind.is_trivia() => 0,
+        _ => 1,
+    })?;
     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
-        return doc_comment.get_definition_with_descend_at(
-            sema,
-            position.offset,
-            |def, _, link_range| {
-                let nav = def.try_to_nav(db)?;
-                Some(RangeInfo::new(link_range, vec![nav]))
-            },
-        );
+        return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| {
+            let nav = def.try_to_nav(db)?;
+            Some(RangeInfo::new(link_range, vec![nav]))
+        });
     }
     let navs = sema
-        .descend_into_macros(original_token.clone())
+        .descend_into_macros(original_token.clone(), offset)
         .into_iter()
         .filter_map(|token| {
             let parent = token.parent()?;
             if let Some(tt) = ast::TokenTree::cast(parent) {
-                if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id)
-                {
+                if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) {
                     return Some(vec![x]);
                 }
             }
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 37166bdbd0c..544c6b42317 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -22,20 +22,19 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
 // image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
 pub(crate) fn goto_implementation(
     db: &RootDatabase,
-    position: FilePosition,
+    FilePosition { file_id, offset }: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
     let sema = Semantics::new(db);
-    let source_file = sema.parse(position.file_id);
+    let source_file = sema.parse(file_id);
     let syntax = source_file.syntax().clone();
 
-    let original_token =
-        pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
-            IDENT | T![self] | INT_NUMBER => 1,
-            _ => 0,
-        })?;
+    let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
+        IDENT | T![self] | INT_NUMBER => 1,
+        _ => 0,
+    })?;
     let range = original_token.text_range();
     let navs =
-        sema.descend_into_macros(original_token)
+        sema.descend_into_macros(original_token, offset)
             .into_iter()
             .filter_map(|token| token.parent().and_then(ast::NameLike::cast))
             .filter_map(|node| match &node {
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 6048990f749..955923d7691 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -16,13 +16,13 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
 // image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
 pub(crate) fn goto_type_definition(
     db: &RootDatabase,
-    position: FilePosition,
+    FilePosition { file_id, offset }: FilePosition,
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
     let sema = hir::Semantics::new(db);
 
-    let file: ast::SourceFile = sema.parse(position.file_id);
+    let file: ast::SourceFile = sema.parse(file_id);
     let token: SyntaxToken =
-        pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
+        pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
             IDENT | INT_NUMBER | T![self] => 2,
             kind if kind.is_trivia() => 0,
             _ => 1,
@@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition(
         }
     };
     let range = token.text_range();
-    sema.descend_into_macros(token)
+    sema.descend_into_macros(token, offset)
         .into_iter()
         .filter_map(|token| {
             let ty = sema
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 43e89a334bf..46a0464e9e6 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -15,6 +15,7 @@ use syntax::{
     SyntaxKind::{self, IDENT, INT_NUMBER},
     SyntaxNode, SyntaxToken, TextRange, T,
 };
+use text_edit::TextSize;
 
 use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav};
 
@@ -51,7 +52,7 @@ pub struct HighlightRelatedConfig {
 pub(crate) fn highlight_related(
     sema: &Semantics<'_, RootDatabase>,
     config: HighlightRelatedConfig,
-    FilePosition { offset, file_id }: FilePosition,
+    pos @ FilePosition { offset, file_id }: FilePosition,
 ) -> Option<Vec<HighlightedRange>> {
     let _p = profile::span("highlight_related");
     let syntax = sema.parse(file_id).syntax().clone();
@@ -79,7 +80,7 @@ pub(crate) fn highlight_related(
         }
         T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
         T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
-        _ if config.references => highlight_references(sema, &syntax, token, file_id),
+        _ if config.references => highlight_references(sema, &syntax, token, pos),
         _ => None,
     }
 }
@@ -129,9 +130,9 @@ fn highlight_references(
     sema: &Semantics<'_, RootDatabase>,
     node: &SyntaxNode,
     token: SyntaxToken,
-    file_id: FileId,
+    FilePosition { file_id, offset }: FilePosition,
 ) -> Option<Vec<HighlightedRange>> {
-    let defs = find_defs(sema, token.clone());
+    let defs = find_defs(sema, token.clone(), offset);
     let usages = defs
         .iter()
         .filter_map(|&d| {
@@ -455,8 +456,12 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange
     }
 }
 
-fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
-    sema.descend_into_macros(token)
+fn find_defs(
+    sema: &Semantics<'_, RootDatabase>,
+    token: SyntaxToken,
+    offset: TextSize,
+) -> FxHashSet<Definition> {
+    sema.descend_into_macros(token, offset)
         .into_iter()
         .filter_map(|token| IdentClass::classify_token(sema, &token))
         .map(IdentClass::definitions_no_ops)
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 40659e6c263..21934b9480e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -162,9 +162,9 @@ fn hover_simple(
     // prefer descending the same token kind in attribute expansions, in normal macros text
     // equivalency is more important
     let descended = if in_attr {
-        [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
+        [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into()
     } else {
-        sema.descend_into_macros_with_same_text(original_token.clone())
+        sema.descend_into_macros_with_same_text(original_token.clone(), offset)
     };
     let descended = || descended.iter();
 
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index ddc71dffa8a..d0f9f7b0e16 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1556,6 +1556,49 @@ fn test_hover_function_show_types() {
     );
 }
 
+#[test]
+fn test_hover_function_associated_type_params() {
+    check(
+        r#"
+trait Foo { type Bar; }
+impl Foo for i32 { type Bar = i64; }
+fn foo(arg: <i32 as Foo>::Bar) {}
+fn main() { foo$0; }
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            fn foo(arg: <i32 as Foo>::Bar)
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+trait Foo<T> { type Bar<U>; }
+impl Foo<i64> for i32 { type Bar<U> = i32; }
+fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {}
+fn main() { foo$0; }
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>)
+            ```
+        "#]],
+    );
+}
+
 #[test]
 fn test_hover_function_pointer_show_identifiers() {
     check(
@@ -3292,7 +3335,50 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
             ```
 
             ```rust
-            struct ST<const C: usize, T = Foo>
+            struct ST<const C: usize = 1, T = Foo>
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn const_generic_default_value() {
+    check(
+        r#"
+struct Foo;
+struct S$0T<const C: usize = {40 + 2}, T = Foo>(T);
+"#,
+        expect![[r#"
+            *ST*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            struct ST<const C: usize = {const}, T = Foo>
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn const_generic_default_value_2() {
+    check(
+        r#"
+struct Foo;
+const VAL = 1;
+struct S$0T<const C: usize = VAL, T = Foo>(T);
+"#,
+        expect![[r#"
+            *ST*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            struct ST<const C: usize = VAL, T = Foo>
             ```
         "#]],
     );
@@ -6469,3 +6555,22 @@ fn test() {
         "#]],
     );
 }
+
+#[test]
+fn generic_params_disabled_by_cfg() {
+    check(
+        r#"
+struct S<#[cfg(never)] T>;
+fn test() {
+    let s$0: S = S;
+}
+"#,
+        expect![[r#"
+            *s*
+
+            ```rust
+            let s: S // size = 0, align = 1
+            ```
+        "#]],
+    );
+}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index bf77d55d58e..c9cdbff7d7d 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -484,7 +484,7 @@ impl Analysis {
         sysroot: Option<&OsStr>,
     ) -> Cancellable<doc_links::DocumentationLinks> {
         self.with_db(|db| {
-            doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default()
+            doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
         })
     }
 
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 17f3771b1a5..2ca2b5b1d5f 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -99,7 +99,7 @@ pub(crate) fn moniker(
         });
     }
     let navs = sema
-        .descend_into_macros(original_token.clone())
+        .descend_into_macros(original_token.clone(), offset)
         .into_iter()
         .filter_map(|token| {
             IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index d1479dd1e58..0740bfbc7b1 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -175,8 +175,12 @@ impl TryToNav for FileSymbol {
 
         Some(NavigationTarget {
             file_id: full_range.file_id,
-            name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() },
-            alias: if self.is_alias { Some(self.name.clone()) } else { None },
+            name: self
+                .is_alias
+                .then(|| self.def.name(db))
+                .flatten()
+                .map_or_else(|| self.name.clone(), |it| it.to_smol_str()),
+            alias: self.is_alias.then(|| self.name.clone()),
             kind: Some(hir::ModuleDefId::from(self.def).into()),
             full_range: full_range.range,
             focus_range,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 813f9ed943f..2d0295692ac 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>(
         )
     });
     token.map(|token| {
-        sema.descend_into_macros_with_same_text(token)
+        sema.descend_into_macros_with_same_text(token, offset)
             .into_iter()
             .filter_map(|it| ast::NameLike::cast(it.parent()?))
             .filter_map(move |name_like| {
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 7795be54e26..847ab3d21ad 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -67,17 +67,20 @@ impl SignatureHelp {
 }
 
 /// Computes parameter information for the given position.
-pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> {
+pub(crate) fn signature_help(
+    db: &RootDatabase,
+    FilePosition { file_id, offset }: FilePosition,
+) -> Option<SignatureHelp> {
     let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id);
+    let file = sema.parse(file_id);
     let file = file.syntax();
     let token = file
-        .token_at_offset(position.offset)
+        .token_at_offset(offset)
         .left_biased()
         // if the cursor is sandwiched between two space tokens and the call is unclosed
         // this prevents us from leaving the CallExpression
         .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
-    let token = sema.descend_into_macros_single(token);
+    let token = sema.descend_into_macros_single(token, offset);
 
     for node in token.parent_ancestors() {
         match_ast! {
@@ -202,7 +205,7 @@ fn signature_help_for_call(
     res.signature.push('(');
     {
         if let Some((self_param, _)) = callable.receiver_param(db) {
-            format_to!(res.signature, "{}", self_param)
+            format_to!(res.signature, "{}", self_param.display(db))
         }
         let mut buf = String::new();
         for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
@@ -1314,6 +1317,25 @@ id! {
         );
     }
 
+    #[test]
+    fn fn_signature_for_method_call_defined_in_macro() {
+        check(
+            r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+struct S;
+id! {
+    impl S {
+        fn foo<'a>(&'a mut self) {}
+    }
+}
+fn test() { S.foo($0); }
+"#,
+            expect![[r#"
+                fn foo(&'a mut self)
+            "#]],
+        );
+    }
+
     #[test]
     fn call_info_for_lambdas() {
         check(
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index ae97236409e..bb01c81d66f 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -395,10 +395,10 @@ fn traverse(
                 NodeOrToken::Token(token) if token.kind() != COMMENT => {
                     let token = match attr_or_derive_item {
                         Some(AttrOrDerive::Attr(_)) => {
-                            sema.descend_into_macros_with_kind_preference(token)
+                            sema.descend_into_macros_with_kind_preference(token, 0.into())
                         }
                         Some(AttrOrDerive::Derive(_)) | None => {
-                            sema.descend_into_macros_single(token)
+                            sema.descend_into_macros_single(token, 0.into())
                         }
                     };
                     match token.parent().and_then(ast::NameLike::cast) {
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 62b2accf5cd..7b9bb61e696 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -961,6 +961,7 @@ impl TtTreeSink<'_> {
                 if has_pseudo_dot {
                     assert!(right.is_empty(), "{left}.{right}");
                 } else {
+                    assert!(!right.is_empty(), "{left}.{right}");
                     self.inner.start_node(SyntaxKind::NAME_REF);
                     self.inner.token(SyntaxKind::INT_NUMBER, right);
                     self.inner.finish_node();
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 333318f53b7..6a2a9adce15 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -184,7 +184,9 @@ pub(crate) mod entry {
             };
             p.bump_any();
             while !p.at(EOF) && !p.at(closing_paren_kind) {
-                expressions::expr(p);
+                if expressions::expr(p).is_none() {
+                    break;
+                }
                 if !p.at(EOF) && !p.at(closing_paren_kind) {
                     p.expect(T![,]);
                 }
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 8ed1c84c4c6..29d9b05d3f3 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -88,7 +88,7 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
 
         // test const_param_default_path
         // struct A<const N: i32 = i32::MAX>;
-        generic_args::const_arg_expr(p);
+        generic_args::const_arg(p);
     }
 
     m.complete(p, CONST_PARAM);
diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs
index 53cdad64992..2c47e3d086d 100644
--- a/crates/parser/src/shortcuts.rs
+++ b/crates/parser/src/shortcuts.rs
@@ -46,12 +46,16 @@ impl LexedStr<'_> {
                     // Tag the token as joint if it is float with a fractional part
                     // we use this jointness to inform the parser about what token split
                     // event to emit when we encounter a float literal in a field access
-                    if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') {
-                        res.was_joint();
+                    if kind == SyntaxKind::FLOAT_NUMBER {
+                        if !self.text(i).ends_with('.') {
+                            res.was_joint();
+                        } else {
+                            was_joint = false;
+                        }
+                    } else {
+                        was_joint = true;
                     }
                 }
-
-                was_joint = true;
             }
         }
         res
@@ -204,6 +208,7 @@ impl Builder<'_, '_> {
                     assert!(right.is_empty(), "{left}.{right}");
                     self.state = State::Normal;
                 } else {
+                    assert!(!right.is_empty(), "{left}.{right}");
                     (self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF });
                     (self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: right });
                     (self.sink)(StrStep::Exit);
diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
index 809ad1b8d5b..49f163b164a 100644
--- a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
+++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
@@ -20,7 +20,8 @@ SOURCE_FILE
                 IDENT "i32"
         WHITESPACE " "
         EQ "="
-      WHITESPACE " "
+        WHITESPACE " "
+        CONST_ARG
       COMMA ","
       WHITESPACE " "
       CONST_PARAM
@@ -37,8 +38,9 @@ SOURCE_FILE
                 IDENT "i32"
         WHITESPACE " "
         EQ "="
+        CONST_ARG
       R_ANGLE ">"
     SEMICOLON ";"
   WHITESPACE "\n"
-error 23: expected a generic const argument
+error 24: expected a generic const argument
 error 40: expected a generic const argument
diff --git a/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast b/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
index 11002bf98d0..3f5fb47d287 100644
--- a/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
+++ b/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
@@ -21,16 +21,17 @@ SOURCE_FILE
         WHITESPACE " "
         EQ "="
         WHITESPACE " "
-        PATH_EXPR
-          PATH
+        CONST_ARG
+          PATH_EXPR
             PATH
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "i32"
+              COLON2 "::"
               PATH_SEGMENT
                 NAME_REF
-                  IDENT "i32"
-            COLON2 "::"
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "MAX"
+                  IDENT "MAX"
       R_ANGLE ">"
     SEMICOLON ";"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast b/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
index 0607ff54fbb..d6501137498 100644
--- a/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
+++ b/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
@@ -21,14 +21,15 @@ SOURCE_FILE
         WHITESPACE " "
         EQ "="
         WHITESPACE " "
-        BLOCK_EXPR
-          STMT_LIST
-            L_CURLY "{"
-            WHITESPACE " "
-            LITERAL
-              INT_NUMBER "1"
-            WHITESPACE " "
-            R_CURLY "}"
+        CONST_ARG
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              LITERAL
+                INT_NUMBER "1"
+              WHITESPACE " "
+              R_CURLY "}"
       R_ANGLE ">"
     SEMICOLON ";"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast b/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
index 8e52313651c..6de10353bf0 100644
--- a/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
+++ b/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
@@ -21,10 +21,11 @@ SOURCE_FILE
         WHITESPACE " "
         EQ "="
         WHITESPACE " "
-        PREFIX_EXPR
-          MINUS "-"
-          LITERAL
-            INT_NUMBER "1"
+        CONST_ARG
+          PREFIX_EXPR
+            MINUS "-"
+            LITERAL
+              INT_NUMBER "1"
       R_ANGLE ">"
     SEMICOLON ";"
   WHITESPACE "\n"
diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs
index 71303d5a631..814a0257402 100644
--- a/crates/profile/src/stop_watch.rs
+++ b/crates/profile/src/stop_watch.rs
@@ -10,13 +10,13 @@ pub struct StopWatch {
     time: Instant,
     #[cfg(target_os = "linux")]
     counter: Option<perf_event::Counter>,
-    memory: Option<MemoryUsage>,
+    memory: MemoryUsage,
 }
 
 pub struct StopWatchSpan {
     pub time: Duration,
     pub instructions: Option<u64>,
-    pub memory: Option<MemoryUsage>,
+    pub memory: MemoryUsage,
 }
 
 impl StopWatch {
@@ -45,20 +45,16 @@ impl StopWatch {
                 None
             }
         };
+        let memory = MemoryUsage::now();
         let time = Instant::now();
         StopWatch {
             time,
             #[cfg(target_os = "linux")]
             counter,
-            memory: None,
+            memory,
         }
     }
-    pub fn memory(mut self, yes: bool) -> StopWatch {
-        if yes {
-            self.memory = Some(MemoryUsage::now());
-        }
-        self
-    }
+
     pub fn elapsed(&mut self) -> StopWatchSpan {
         let time = self.time.elapsed();
 
@@ -69,7 +65,7 @@ impl StopWatch {
         #[cfg(not(target_os = "linux"))]
         let instructions = None;
 
-        let memory = self.memory.map(|it| MemoryUsage::now() - it);
+        let memory = MemoryUsage::now() - self.memory;
         StopWatchSpan { time, instructions, memory }
     }
 }
@@ -93,9 +89,7 @@ impl fmt::Display for StopWatchSpan {
             }
             write!(f, ", {instructions}{prefix}instr")?;
         }
-        if let Some(memory) = self.memory {
-            write!(f, ", {memory}")?;
-        }
+        write!(f, ", {}", self.memory)?;
         Ok(())
     }
 }
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index f51ea7eeb22..13463e9f72e 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -167,7 +167,8 @@ impl ProjectWorkspace {
                 cmd.envs(&config.extra_env);
                 cmd.arg("--version").current_dir(current_dir);
                 cmd
-            })?;
+            })
+            .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
             anyhow::Ok(
                 cargo_version
                     .get(prefix.len()..)
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 5bfac7ee45c..1f9d6db9314 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ crossbeam-channel = "0.5.5"
 dissimilar = "1.0.4"
 itertools = "0.10.5"
 scip = "0.1.1"
-lsp-types = { version = "=0.94", features = ["proposed"] }
+lsp-types = { version = "=0.94.0", features = ["proposed"] }
 parking_lot = "0.12.1"
 xflags = "0.3.0"
 oorandom = "11.1.3"
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index f446a7c0596..4a03be1893c 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -235,9 +235,7 @@ impl flags::AnalysisStats {
         if let Some(instructions) = total_span.instructions {
             report_metric("total instructions", instructions, "#instr");
         }
-        if let Some(memory) = total_span.memory {
-            report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
-        }
+        report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB");
 
         if env::var("RA_COUNT").is_ok() {
             eprintln!("{}", profile::countme::get_all());
@@ -257,7 +255,7 @@ impl flags::AnalysisStats {
             eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
         }
 
-        if self.memory_usage && verbosity.is_verbose() {
+        if verbosity.is_verbose() {
             print_memory_usage(host, vfs);
         }
 
@@ -814,7 +812,7 @@ impl flags::AnalysisStats {
     }
 
     fn stop_watch(&self) -> StopWatch {
-        StopWatch::start().memory(self.memory_usage)
+        StopWatch::start()
     }
 }
 
diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs
index 13b7f039bb0..419440b6df7 100644
--- a/crates/rust-analyzer/src/cli/flags.rs
+++ b/crates/rust-analyzer/src/cli/flags.rs
@@ -62,8 +62,6 @@ xflags::xflags! {
             optional --randomize
             /// Run type inference in parallel.
             optional --parallel
-            /// Collect memory usage statistics.
-            optional --memory-usage
             /// Print the total length of all source and macro files (whitespace is not counted).
             optional --source-stats
 
@@ -191,7 +189,6 @@ pub struct AnalysisStats {
     pub output: Option<OutputFormat>,
     pub randomize: bool,
     pub parallel: bool,
-    pub memory_usage: bool,
     pub source_stats: bool,
     pub only: Option<String>,
     pub with_deps: bool,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index fa20c796ec2..40c50f6d176 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -150,18 +150,22 @@ config_data! {
         ///
         /// Set to `"all"` to pass `--all-features` to Cargo.
         check_features | checkOnSave_features: Option<CargoFeaturesDef>  = "null",
+        /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+        ///
+        /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+        check_ignore: FxHashSet<String> = "[]",
         /// Specifies the working directory for running checks.
         /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
         // FIXME: Ideally we would support this in some way
-        ///   This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
+        ///   This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.
         /// - "root": run checks in the project's root directory.
-        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+        /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
         /// is set.
         check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
-        /// Specifies the invocation strategy to use when running the checkOnSave command.
+        /// Specifies the invocation strategy to use when running the check command.
         /// If `per_workspace` is set, the command will be executed for each workspace.
         /// If `once` is set, the command will be executed once.
-        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+        /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
         /// is set.
         check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
         /// Whether to pass `--no-default-features` to Cargo. Defaults to
@@ -1098,6 +1102,7 @@ impl Config {
             remap_prefix: self.data.diagnostics_remapPrefix.clone(),
             warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
             warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
+            check_ignore: self.data.check_ignore.clone(),
         }
     }
 
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 33422fd058e..b65f38a0c71 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -6,6 +6,7 @@ use std::mem;
 use ide::FileId;
 use ide_db::FxHashMap;
 use nohash_hasher::{IntMap, IntSet};
+use rustc_hash::FxHashSet;
 use triomphe::Arc;
 
 use crate::lsp_ext;
@@ -17,6 +18,7 @@ pub struct DiagnosticsMapConfig {
     pub remap_prefix: FxHashMap<String, String>,
     pub warnings_as_info: Vec<String>,
     pub warnings_as_hint: Vec<String>,
+    pub check_ignore: FxHashSet<String>,
 }
 
 #[derive(Debug, Default, Clone)]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e1d1130ff1b..06564578d80 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -292,6 +292,13 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
 
     let mut source = String::from("rustc");
     let mut code = rd.code.as_ref().map(|c| c.code.clone());
+
+    if let Some(code_val) = &code {
+        if config.check_ignore.contains(code_val) {
+            return Vec::new();
+        }
+    }
+
     if let Some(code_val) = &code {
         // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
         let scoped_code: Vec<&str> = code_val.split("::").collect();
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 138ddd20897..ea7ebd85b33 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -296,7 +296,7 @@ TypeParam =
 
 ConstParam =
   Attr* 'const' Name ':' Type
-  ('=' default_val:Expr)?
+  ('=' default_val:ConstArg)?
 
 LifetimeParam =
   Attr* Lifetime (':' TypeBoundList?)?
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 0b27faa535d..16448db04f8 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -709,7 +709,7 @@ impl ConstParam {
     pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
     pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
     pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
-    pub fn default_val(&self) -> Option<Expr> { support::child(&self.syntax) }
+    pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 4c6db0ef06c..17e311c0c50 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -503,11 +503,16 @@ pub fn hacky_block_expr(
 pub fn expr_unit() -> ast::Expr {
     expr_from_text("()")
 }
+
 pub fn expr_literal(text: &str) -> ast::Literal {
     assert_eq!(text.trim(), text);
     ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
 }
 
+pub fn expr_const_value(text: &str) -> ast::ConstArg {
+    ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
+}
+
 pub fn expr_empty_block() -> ast::Expr {
     expr_from_text("{}")
 }
@@ -1100,7 +1105,7 @@ pub mod tokens {
 
     pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
         SourceFile::parse(
-            "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
+            "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
         )
     });
 
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 3308077da5b..691d0c618f3 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -61,6 +61,14 @@ impl ast::BlockExpr {
     pub fn tail_expr(&self) -> Option<ast::Expr> {
         self.stmt_list()?.tail_expr()
     }
+    /// Block expressions accept outer and inner attributes, but only when they are the outer
+    /// expression of an expression statement or the final expression of another block expression.
+    pub fn may_carry_attributes(&self) -> bool {
+        matches!(
+            self.syntax().parent().map(|it| it.kind()),
+            Some(SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT)
+        )
+    }
 }
 
 #[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 4cd668a0cd5..27c8a13e58d 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -195,11 +195,16 @@ impl ast::TokenTree {
                     // Tag the token as joint if it is float with a fractional part
                     // we use this jointness to inform the parser about what token split
                     // event to emit when we encounter a float literal in a field access
-                    if kind == SyntaxKind::FLOAT_NUMBER && !t.text().ends_with('.') {
-                        parser_input.was_joint();
+                    if kind == SyntaxKind::FLOAT_NUMBER {
+                        if !t.text().ends_with('.') {
+                            parser_input.was_joint();
+                        } else {
+                            was_joint = false;
+                        }
+                    } else {
+                        was_joint = true;
                     }
                 }
-                was_joint = true;
             }
         }
 
@@ -250,6 +255,7 @@ impl ast::TokenTree {
                             if has_pseudo_dot {
                                 assert!(right.is_empty(), "{left}.{right}");
                             } else {
+                                assert!(!right.is_empty(), "{left}.{right}");
                                 builder.start_node(SyntaxKind::NAME_REF);
                                 builder.token(SyntaxKind::INT_NUMBER, right);
                                 builder.finish_node();
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index ea00c9540ff..71feed0f72c 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -161,23 +161,30 @@ List of features to activate. Defaults to
 
 Set to `"all"` to pass `--all-features` to Cargo.
 --
+[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`)::
++
+--
+List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+
+For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+--
 [[rust-analyzer.check.invocationLocation]]rust-analyzer.check.invocationLocation (default: `"workspace"`)::
 +
 --
 Specifies the working directory for running checks.
 - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
-  This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
+  This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.
 - "root": run checks in the project's root directory.
-This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
 is set.
 --
 [[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`)::
 +
 --
-Specifies the invocation strategy to use when running the checkOnSave command.
+Specifies the invocation strategy to use when running the check command.
 If `per_workspace` is set, the command will be executed for each workspace.
 If `once` is set, the command will be executed once.
-This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
 is set.
 --
 [[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`)::
diff --git a/editors/code/package.json b/editors/code/package.json
index 76d7e91f381..44f1b81675a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -292,6 +292,11 @@
                 "command": "rust-analyzer.viewMemoryLayout",
                 "title": "View Memory Layout",
                 "category": "rust-analyzer"
+            },
+            {
+                "command": "rust-analyzer.toggleCheckOnSave",
+                "title": "Toggle Check on Save",
+                "category": "rust-analyzer"
             }
         ],
         "keybindings": [
@@ -700,8 +705,17 @@
                         }
                     ]
                 },
+                "rust-analyzer.check.ignore": {
+                    "markdownDescription": "List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.\n\nFor example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...",
+                    "default": [],
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    },
+                    "uniqueItems": true
+                },
                 "rust-analyzer.check.invocationLocation": {
-                    "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n    This falls back to \"root\" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
+                    "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n    This falls back to \"root\" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.",
                     "default": "workspace",
                     "type": "string",
                     "enum": [
@@ -714,7 +728,7 @@
                     ]
                 },
                 "rust-analyzer.check.invocationStrategy": {
-                    "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
+                    "markdownDescription": "Specifies the invocation strategy to use when running the check command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.",
                     "default": "per_workspace",
                     "type": "string",
                     "enum": [
diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts
index ef4dff095cf..6cf399599d9 100644
--- a/editors/code/src/bootstrap.ts
+++ b/editors/code/src/bootstrap.ts
@@ -20,7 +20,7 @@ export async function bootstrap(
 
     log.info("Using server binary at", path);
 
-    if (!isValidExecutable(path)) {
+    if (!isValidExecutable(path, config.serverExtraEnv)) {
         if (config.serverPath) {
             throw new Error(`Failed to execute ${path} --version. \`config.server.path\` or \`config.serverPath\` has been set explicitly.\
             Consider removing this config or making a valid server binary available at that path.`);
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index e21f536f26a..aba37bac27d 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -1407,3 +1407,10 @@ locate()
         ctx.pushExtCleanup(document);
     };
 }
+
+export function toggleCheckOnSave(ctx: Ctx): Cmd {
+    return async () => {
+        await ctx.config.toggleCheckOnSave();
+        ctx.refreshServerStatus();
+    };
+}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 0e64054c11d..39e2f767c76 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -216,6 +216,39 @@ export class Config {
             ),
         );
     }
+    get checkOnSave() {
+        return this.get<boolean>("checkOnSave") ?? false;
+    }
+    async toggleCheckOnSave() {
+        const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" };
+        let overrideInLanguage;
+        let target;
+        let value;
+        if (
+            config.workspaceFolderValue !== undefined ||
+            config.workspaceFolderLanguageValue !== undefined
+        ) {
+            target = vscode.ConfigurationTarget.WorkspaceFolder;
+            overrideInLanguage = config.workspaceFolderLanguageValue;
+            value = config.workspaceFolderValue || config.workspaceFolderLanguageValue;
+        } else if (
+            config.workspaceValue !== undefined ||
+            config.workspaceLanguageValue !== undefined
+        ) {
+            target = vscode.ConfigurationTarget.Workspace;
+            overrideInLanguage = config.workspaceLanguageValue;
+            value = config.workspaceValue || config.workspaceLanguageValue;
+        } else if (config.globalValue !== undefined || config.globalLanguageValue !== undefined) {
+            target = vscode.ConfigurationTarget.Global;
+            overrideInLanguage = config.globalLanguageValue;
+            value = config.globalValue || config.globalLanguageValue;
+        } else if (config.defaultValue !== undefined || config.defaultLanguageValue !== undefined) {
+            overrideInLanguage = config.defaultLanguageValue;
+            value = config.defaultValue || config.defaultLanguageValue;
+        }
+        await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage);
+    }
+
     get traceExtension() {
         return this.get<boolean>("trace.extension");
     }
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 16c14ca54f2..363a7a82e68 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -94,6 +94,7 @@ export class Ctx {
     private unlinkedFiles: vscode.Uri[];
     private _dependencies: RustDependenciesProvider | undefined;
     private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
+    private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
 
     get client() {
         return this._client;
@@ -404,7 +405,15 @@ export class Ctx {
     }
 
     setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
+        this.lastStatus = status;
+        this.updateStatusBarItem();
+    }
+    refreshServerStatus() {
+        this.updateStatusBarItem();
+    }
+    private updateStatusBarItem() {
         let icon = "";
+        const status = this.lastStatus;
         const statusBar = this.statusBar;
         statusBar.show();
         statusBar.tooltip = new vscode.MarkdownString("", true);
@@ -447,13 +456,18 @@ export class Ctx {
                     "statusBarItem.warningBackground",
                 );
                 statusBar.command = "rust-analyzer.startServer";
-                statusBar.text = `$(stop-circle) rust-analyzer`;
+                statusBar.text = "$(stop-circle) rust-analyzer";
                 return;
         }
         if (statusBar.tooltip.value) {
             statusBar.tooltip.appendMarkdown("\n\n---\n\n");
         }
-        statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
+        statusBar.tooltip.appendMarkdown("\n\n[Open Logs](command:rust-analyzer.openLogs)");
+        statusBar.tooltip.appendMarkdown(
+            `\n\n[${
+                this.config.checkOnSave ? "Disable" : "Enable"
+            } Check on Save](command:rust-analyzer.toggleCheckOnSave)`,
+        );
         statusBar.tooltip.appendMarkdown(
             "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)",
         );
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 448150bac06..ee5e5b1b80c 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -180,6 +180,7 @@ function createCommands(): Record<string, CommandFactory> {
         ssr: { enabled: commands.ssr },
         serverVersion: { enabled: commands.serverVersion },
         viewMemoryLayout: { enabled: commands.viewMemoryLayout },
+        toggleCheckOnSave: { enabled: commands.toggleCheckOnSave },
         // Internal commands which are invoked by the server.
         applyActionGroup: { enabled: commands.applyActionGroup },
         applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 38ce6761578..0414ea0f806 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
 import { strict as nativeAssert } from "assert";
 import { exec, type ExecOptions, spawnSync } from "child_process";
 import { inspect } from "util";
+import type { Env } from "./client";
 
 export function assert(condition: boolean, explanation: string): asserts condition {
     try {
@@ -93,10 +94,13 @@ export function isDocumentInWorkspace(document: RustDocument): boolean {
     return false;
 }
 
-export function isValidExecutable(path: string): boolean {
+export function isValidExecutable(path: string, extraEnv: Env): boolean {
     log.debug("Checking availability of a binary at", path);
 
-    const res = spawnSync(path, ["--version"], { encoding: "utf8" });
+    const res = spawnSync(path, ["--version"], {
+        encoding: "utf8",
+        env: { ...process.env, ...extraEnv },
+    });
 
     const printOutput = res.error ? log.warn : log.info;
     printOutput(path, "--version:", res);
@@ -151,6 +155,7 @@ export function execute(command: string, options: ExecOptions): Promise<string>
 }
 
 export function executeDiscoverProject(command: string, options: ExecOptions): Promise<string> {
+    options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options);
     log.info(`running command: ${command}`);
     return new Promise((resolve, reject) => {
         exec(command, options, (err, stdout, _) => {
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml
index 01707d30191..e1c49db39d5 100644
--- a/lib/lsp-server/Cargo.toml
+++ b/lib/lsp-server/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "lsp-server"
-version = "0.7.2"
+version = "0.7.3"
 description = "Generic LSP server scaffold."
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server"
@@ -9,7 +9,8 @@ edition = "2021"
 [dependencies]
 log = "0.4.17"
 serde_json = "1.0.96"
-serde = { version = "1.0.156", features = ["derive"] }
+# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde
+serde = { version = "1.0.156, < 1.0.172", features = ["derive"] }
 crossbeam-channel = "0.5.6"
 
 [dev-dependencies]
diff --git a/lib/lsp-server/src/stdio.rs b/lib/lsp-server/src/stdio.rs
index 49a825e579b..e487b9b4622 100644
--- a/lib/lsp-server/src/stdio.rs
+++ b/lib/lsp-server/src/stdio.rs
@@ -3,6 +3,8 @@ use std::{
     thread,
 };
 
+use log::debug;
+
 use crossbeam_channel::{bounded, Receiver, Sender};
 
 use crate::Message;
@@ -23,7 +25,8 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
         while let Some(msg) = Message::read(&mut stdin)? {
             let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit());
 
-            reader_sender.send(msg).unwrap();
+            debug!("sending message {:#?}", msg);
+            reader_sender.send(msg).expect("receiver was dropped, failed to send a message");
 
             if is_exit {
                 break;
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 68537423195..e4710260409 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -103,9 +103,7 @@ impl Metrics {
         path: &str,
     ) -> anyhow::Result<()> {
         eprintln!("\nMeasuring analysis-stats/{name}");
-        let output =
-            cmd!(sh, "./target/release/rust-analyzer -q analysis-stats --memory-usage {path}")
-                .read()?;
+        let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?;
         for (metric, value, unit) in parse_metrics(&output) {
             self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into());
         }