about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/hir-def
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
committerLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
commitb8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5 (patch)
tree5adcbc6cf50af3bebc2cd4f42d5252a4d728690e /src/tools/rust-analyzer/crates/hir-def
parent51f714c8c5021fe25442e46798b1cbef2f2249ed (diff)
parentaa9bc8612514d216f84eec218dfd19ab83f3598a (diff)
downloadrust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.tar.gz
rust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.zip
Merge commit 'aa9bc8612514d216f84eec218dfd19ab83f3598a' into sync-from-ra
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-def')
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs241
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs)166
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs370
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs1058
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs137
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs246
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/adt.rs)73
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs51
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/keys.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs211
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/expr.rs)148
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs)28
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs98
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs293
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/layout.rs97
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs154
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lower.rs45
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs300
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs101
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs311
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs)85
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs275
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs609
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs141
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs256
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs89
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs114
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/test_db.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/visibility.rs3
57 files changed, 4355 insertions, 2091 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 31d4018d2b6..83c7051646e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
 [dependencies]
 anymap = "1.0.0-beta.2"
 arrayvec = "0.7.2"
-bitflags = "1.3.2"
+bitflags = "2.1.0"
 cov-mark = "2.0.0-pre.1"
 # We need to freeze the version of the crate, as the raw-api feature is considered unstable
 dashmap = { version = "=5.4.0", features = ["raw-api"] }
@@ -29,6 +29,7 @@ once_cell = "1.17.0"
 rustc-hash = "1.1.0"
 smallvec.workspace = true
 tracing = "0.1.35"
+triomphe.workspace = true
 
 rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
 rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index 200072c172e..bab3bbc2329 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -1,6 +1,11 @@
 //! A higher level attributes based on TokenTree, with also some shortcuts.
 
-use std::{hash::Hash, ops, sync::Arc};
+pub mod builtin;
+
+#[cfg(test)]
+mod tests;
+
+use std::{hash::Hash, ops};
 
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
@@ -16,14 +21,16 @@ use syntax::{
     ast::{self, HasAttrs, IsString},
     AstPtr, AstToken, SmolStr, TextRange, TextSize,
 };
+use triomphe::Arc;
 
 use crate::{
     db::DefDatabase,
     item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
+    lang_item::LangItem,
     nameres::{ModuleOrigin, ModuleSource},
     src::{HasChildSource, HasSource},
-    AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
-    VariantId,
+    AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
+    LocalFieldId, Lookup, MacroId, VariantId,
 };
 
 /// Holds documentation
@@ -88,6 +95,7 @@ impl Attrs {
         db: &dyn DefDatabase,
         e: EnumId,
     ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
+        let _p = profile::span("variants_attrs_query");
         // FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids
         let mut res = ArenaMap::default();
 
@@ -114,6 +122,7 @@ impl Attrs {
         db: &dyn DefDatabase,
         v: VariantId,
     ) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
+        let _p = profile::span("fields_attrs_query");
         // FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
         let mut res = ArenaMap::default();
 
@@ -175,13 +184,13 @@ impl Attrs {
 
         Arc::new(res)
     }
+}
 
+impl Attrs {
     pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
         AttrQuery { attrs: self, key }
     }
-}
 
-impl Attrs {
     pub fn cfg(&self) -> Option<CfgExpr> {
         let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
         let first = cfgs.next()?;
@@ -193,6 +202,7 @@ impl Attrs {
             None => Some(first),
         }
     }
+
     pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
         match self.cfg() {
             None => true,
@@ -204,6 +214,10 @@ impl Attrs {
         self.by_key("lang").string_value()
     }
 
+    pub fn lang_item(&self) -> Option<LangItem> {
+        self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
+    }
+
     pub fn docs(&self) -> Option<Documentation> {
         let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
         let indent = doc_indent(self);
@@ -238,6 +252,14 @@ impl Attrs {
         })
     }
 
+    pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
+        self.by_key("doc").tt_values().map(DocExpr::parse)
+    }
+
+    pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
+        self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
+    }
+
     pub fn is_proc_macro(&self) -> bool {
         self.by_key("proc_macro").exists()
     }
@@ -249,10 +271,120 @@ impl Attrs {
     pub fn is_proc_macro_derive(&self) -> bool {
         self.by_key("proc_macro_derive").exists()
     }
+
+    pub fn is_unstable(&self) -> bool {
+        self.by_key("unstable").exists()
+    }
+}
+
+use std::slice::Iter as SliceIter;
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub enum DocAtom {
+    /// eg. `#[doc(hidden)]`
+    Flag(SmolStr),
+    /// eg. `#[doc(alias = "x")]`
+    ///
+    /// Note that a key can have multiple values that are all considered "active" at the same time.
+    /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
+    KeyValue { key: SmolStr, value: SmolStr },
+}
+
+// Adapted from `CfgExpr` parsing code
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
+pub enum DocExpr {
+    Invalid,
+    /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
+    Atom(DocAtom),
+    /// eg. `#[doc(alias("x", "y"))]`
+    Alias(Vec<SmolStr>),
+}
+
+impl From<DocAtom> for DocExpr {
+    fn from(atom: DocAtom) -> Self {
+        DocExpr::Atom(atom)
+    }
+}
+
+impl DocExpr {
+    fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr {
+        next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
+    }
+
+    pub fn aliases(&self) -> &[SmolStr] {
+        match self {
+            DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
+                std::slice::from_ref(value)
+            }
+            DocExpr::Alias(aliases) => aliases,
+            _ => &[],
+        }
+    }
+}
+
+fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
+    let name = match it.next() {
+        None => return None,
+        Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
+        Some(_) => return Some(DocExpr::Invalid),
+    };
+
+    // Peek
+    let ret = match it.as_slice().first() {
+        Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
+            match it.as_slice().get(1) {
+                Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
+                    it.next();
+                    it.next();
+                    // FIXME: escape? raw string?
+                    let value =
+                        SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
+                    DocAtom::KeyValue { key: name, value }.into()
+                }
+                _ => return Some(DocExpr::Invalid),
+            }
+        }
+        Some(tt::TokenTree::Subtree(subtree)) => {
+            it.next();
+            let subs = parse_comma_sep(subtree);
+            match name.as_str() {
+                "alias" => DocExpr::Alias(subs),
+                _ => DocExpr::Invalid,
+            }
+        }
+        _ => DocAtom::Flag(name).into(),
+    };
+
+    // Eat comma separator
+    if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
+        if punct.char == ',' {
+            it.next();
+        }
+    }
+    Some(ret)
+}
+
+fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
+    subtree
+        .token_trees
+        .iter()
+        .filter_map(|tt| match tt {
+            tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
+                // FIXME: escape? raw string?
+                Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
+            }
+            _ => None,
+        })
+        .collect()
 }
 
 impl AttrsWithOwner {
-    pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
+    pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
+        Self { attrs: db.attrs(owner), owner }
+    }
+
+    pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
+        let _p = profile::span("attrs_query");
         // FIXME: this should use `Trace` to avoid duplication in `source_map` below
         let raw_attrs = match def {
             AttrDefId::ModuleId(module) => {
@@ -286,31 +418,29 @@ impl AttrsWithOwner {
                 }
             }
             AttrDefId::FieldId(it) => {
-                return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
+                return db.fields_attrs(it.parent)[it.local_id].clone();
             }
             AttrDefId::EnumVariantId(it) => {
-                return Self {
-                    attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
-                    owner: def,
-                };
+                return db.variants_attrs(it.parent)[it.local_id].clone();
             }
+            // FIXME: DRY this up
             AttrDefId::AdtId(it) => match it {
-                AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-                AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-                AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+                AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
+                AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
+                AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
             },
-            AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-            AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
+            AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
             AttrDefId::MacroId(it) => match it {
-                MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
-                MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-                MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+                MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
+                MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
+                MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
             },
-            AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-            AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-            AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-            AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
-            AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
+            AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
+            AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
+            AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
+            AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
             AttrDefId::GenericParamId(it) => match it {
                 GenericParamId::ConstParamId(it) => {
                     let src = it.parent().child_source(db);
@@ -331,11 +461,11 @@ impl AttrsWithOwner {
                     RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id]))
                 }
             },
-            AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
         };
 
         let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
-        Self { attrs: Attrs(attrs), owner: def }
+        Attrs(attrs)
     }
 
     pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@@ -371,7 +501,7 @@ impl AttrsWithOwner {
             AttrDefId::FieldId(id) => {
                 let map = db.fields_attrs_source_map(id.parent);
                 let file_id = id.parent.file_id(db);
-                let root = db.parse_or_expand(file_id).unwrap();
+                let root = db.parse_or_expand(file_id);
                 let owner = match &map[id.local_id] {
                     Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
                     Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
@@ -379,28 +509,28 @@ impl AttrsWithOwner {
                 InFile::new(file_id, owner)
             }
             AttrDefId::AdtId(adt) => match adt {
-                AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-                AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-                AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+                AdtId::StructId(id) => any_has_attrs(db, id),
+                AdtId::UnionId(id) => any_has_attrs(db, id),
+                AdtId::EnumId(id) => any_has_attrs(db, id),
             },
-            AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+            AttrDefId::FunctionId(id) => any_has_attrs(db, id),
             AttrDefId::EnumVariantId(id) => {
                 let map = db.variants_attrs_source_map(id.parent);
                 let file_id = id.parent.lookup(db).id.file_id();
-                let root = db.parse_or_expand(file_id).unwrap();
+                let root = db.parse_or_expand(file_id);
                 InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)))
             }
-            AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-            AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-            AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-            AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-            AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+            AttrDefId::StaticId(id) => any_has_attrs(db, id),
+            AttrDefId::ConstId(id) => any_has_attrs(db, id),
+            AttrDefId::TraitId(id) => any_has_attrs(db, id),
+            AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
+            AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
             AttrDefId::MacroId(id) => match id {
-                MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-                MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
-                MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+                MacroId::Macro2Id(id) => any_has_attrs(db, id),
+                MacroId::MacroRulesId(id) => any_has_attrs(db, id),
+                MacroId::ProcMacroId(id) => any_has_attrs(db, id),
             },
-            AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+            AttrDefId::ImplId(id) => any_has_attrs(db, id),
             AttrDefId::GenericParamId(id) => match id {
                 GenericParamId::ConstParamId(id) => id
                     .parent()
@@ -415,7 +545,7 @@ impl AttrsWithOwner {
                     .child_source(db)
                     .map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
             },
-            AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+            AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
         };
 
         AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
@@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
                 .nth(2);
 
             match name {
-                Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text),
+                Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
                 _ => None
             }
         })
     }
 }
 
-fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
+fn any_has_attrs(
+    db: &dyn DefDatabase,
+    id: impl Lookup<Data = impl HasSource<Value = impl ast::HasAttrs>>,
+) -> InFile<ast::AnyHasAttrs> {
+    id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
+}
+
+fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
     let tree = id.item_tree(db);
     let mod_item = N::id_to_mod_item(id.value);
     tree.raw_attrs(mod_item.into()).clone()
 }
 
+fn attrs_from_item_tree_loc<N: ItemTreeNode>(
+    db: &dyn DefDatabase,
+    lookup: impl Lookup<Data = ItemLoc<N>>,
+) -> RawAttrs {
+    let id = lookup.lookup(db).id;
+    attrs_from_item_tree(db, id)
+}
+
+fn attrs_from_item_tree_assoc<N: ItemTreeNode>(
+    db: &dyn DefDatabase,
+    lookup: impl Lookup<Data = AssocItemLoc<N>>,
+) -> RawAttrs {
+    let id = lookup.lookup(db).id;
+    attrs_from_item_tree(db, id)
+}
+
 pub(crate) fn variants_attrs_source_map(
     db: &dyn DefDatabase,
     def: EnumId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index 142b1229019..cead64a3374 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -2,7 +2,7 @@
 //!
 //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
 //!
-//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7.
+//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6.
 //!
 //! The macros were adjusted to only expand to the attribute name, since that is all we need to do
 //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -108,7 +108,7 @@ macro_rules! experimental {
     };
 }
 
-/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
+/// Attributes that have a special meaning to rustc or rustdoc.
 #[rustfmt::skip]
 pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ==========================================================================
@@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
     ungated!(
         should_panic, Normal,
-        template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing,
+        template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
     ),
     // FIXME(Centril): This can be used on stable but shouldn't.
     ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
@@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     // Lints:
     ungated!(
-        warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+        warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        DuplicatesOk, @only_local: true,
     ),
     ungated!(
-        allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+        allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        DuplicatesOk, @only_local: true,
     ),
     gated!(
         expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
         lint_reasons, experimental!(expect)
     ),
     ungated!(
-        forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+        forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        DuplicatesOk, @only_local: true,
     ),
     ungated!(
-        deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+        deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        DuplicatesOk, @only_local: true,
     ),
     ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
     gated!(
@@ -181,16 +185,17 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ABI, linking, symbols, and FFI
     ungated!(
         link, Normal,
-        template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
+        template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#),
         DuplicatesOk,
     ),
     ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
     ungated!(no_link, Normal, template!(Word), WarnFollowing),
-    ungated!(repr, Normal, template!(List: "C"), DuplicatesOk),
+    ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true),
     ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
     ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
     ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
     ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true),
+    ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding),
 
     // Limits:
     ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
@@ -201,6 +206,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
 
     // Entry point:
+    gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
     ungated!(start, Normal, template!(Word), WarnFollowing),
     ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
     ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
@@ -222,11 +228,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
     ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
     ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing),
-    ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk),
+    ungated!(
+        target_feature, Normal, template!(List: r#"enable = "name""#),
+        DuplicatesOk, @only_local: true,
+    ),
     ungated!(track_caller, Normal, template!(Word), WarnFollowing),
+    ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
     gated!(
         no_sanitize, Normal,
-        template!(List: "address, memory, thread"), DuplicatesOk,
+        template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
         experimental!(no_sanitize)
     ),
     gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
@@ -235,25 +245,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
     ),
 
+    // Debugging
+    ungated!(
+        debugger_visualizer, Normal,
+        template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk
+    ),
+
     // ==========================================================================
     // Unstable attributes:
     // ==========================================================================
 
-    // RFC #3191: #[debugger_visualizer] support
-    gated!(
-        debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
-        DuplicatesOk, experimental!(debugger_visualizer)
-    ),
-
     // Linking:
-    gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
     gated!(
-        link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib,
-        experimental!(link_ordinal)
+        naked, Normal, template!(Word), WarnFollowing, @only_local: true,
+        naked_functions, experimental!(naked)
     ),
 
     // Plugins:
-    // XXX Modified for use in rust-analyzer
     // BuiltinAttribute {
     //     name: sym::plugin,
     //     only_local: false,
@@ -270,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     //         cfg_fn!(plugin)
     //     ),
     // },
-    BuiltinAttribute {
-        name: "plugin",
-        template: template!(List: "name"),
-    },
 
     // Testing:
     gated!(
@@ -282,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     // RFC #1268
     gated!(
-        marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker)
+        marker, Normal, template!(Word), WarnFollowing, @only_local: true,
+        marker_trait_attr, experimental!(marker)
     ),
     gated!(
         thread_local, Normal, template!(Word), WarnFollowing,
@@ -294,11 +299,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute,
         experimental!(optimize),
     ),
-    // RFC 2867
-    gated!(
-        instruction_set, Normal, template!(List: "set"), ErrorPreceding,
-        isa_attribute, experimental!(instruction_set)
-    ),
 
     gated!(
         ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
@@ -306,10 +306,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
     gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
     gated!(
-        register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
-        experimental!(register_attr),
-    ),
-    gated!(
         register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
         experimental!(register_tool),
     ),
@@ -321,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     // RFC 2632
     gated!(
         const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
-        "`const` is a temporary placeholder for marking a trait that is suitable for `const` \
+        "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
         `impls` and all default bodies as `const`, which may be removed or renamed in the \
         future."
     ),
@@ -331,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         experimental!(deprecated_safe),
     ),
 
+    // `#[collapse_debuginfo]`
+    gated!(
+        collapse_debuginfo, Normal, template!(Word), WarnFollowing,
+        experimental!(collapse_debuginfo)
+    ),
+
+    // RFC 2397
+    gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)),
+
+    // `#[cfi_encoding = ""]`
+    gated!(
+        cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
+        experimental!(cfi_encoding)
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
 
-    ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk),
+    ungated!(
+        feature, CrateLevel,
+        template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true,
+    ),
     // DuplicatesOk since it has its own validation
     ungated!(
-        stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk,
+        stable, Normal,
+        template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true,
     ),
     ungated!(
         unstable, Normal,
         template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
     ),
     ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
-    ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
-    ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
+    ungated!(
+        rustc_const_stable, Normal,
+        template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true,
+    ),
+    ungated!(
+        rustc_default_body_unstable, Normal,
+        template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk
+    ),
     gated!(
         allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
         "allow_internal_unstable side-steps feature gating and stability checks",
@@ -360,6 +381,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
         "allow_internal_unsafe side-steps the unsafe_code lint",
     ),
+    ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk),
+    rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
+    "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
+    through unstable paths"),
 
     // ==========================================================================
     // Internal attributes: Type system related:
@@ -377,10 +402,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
     rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
-    gated!(
-        alloc_error_handler, Normal, template!(Word), WarnFollowing,
-        experimental!(alloc_error_handler)
-    ),
+    rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+    rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+    rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
     gated!(
         default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
         experimental!(default_lib_allocator),
@@ -461,6 +485,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
     // to assist in changes to diagnostic APIs.
     rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+    // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
+    // types (as well as any others in future).
+    rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+    // Used by the `rustc::bad_opt_access` lint on fields
+    // types (as well as any others in future).
+    rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Const related:
@@ -504,8 +534,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         "language items are subject to change",
     ),
     rustc_attr!(
-        rustc_pass_by_value, Normal,
-        template!(Word), ErrorFollowing,
+        rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
         "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
     ),
     rustc_attr!(
@@ -513,10 +542,18 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
     ),
     rustc_attr!(
+        rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true,
+        "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
+    ),
+    rustc_attr!(
         rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
         "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
     ),
     rustc_attr!(
+        rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+        "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
+    ),
+    rustc_attr!(
         rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
         "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
          the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
@@ -527,24 +564,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         and it is only intended to be used in `alloc`."
     ),
 
-    // modified for r-a
-    // BuiltinAttribute {
-    //     name: sym::rustc_diagnostic_item,
-    //     // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
-    //     only_local: false,
-    //     type_: Normal,
-    //     template: template!(NameValueStr: "name"),
-    //     duplicates: ErrorFollowing,
-    //     gate: Gated(
-    //         Stability::Unstable,
-    //         sym::rustc_attrs,
-    //         "diagnostic items compiler internal support for linting",
-    //         cfg_fn!(rustc_attrs),
-    //     ),
-    // },
     BuiltinAttribute {
+        // name: sym::rustc_diagnostic_item,
         name: "rustc_diagnostic_item",
+        // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
+        // only_local: false,
+        // type_: Normal,
         template: template!(NameValueStr: "name"),
+        // duplicates: ErrorFollowing,
+        // gate: Gated(
+            // Stability::Unstable,
+            // sym::rustc_attrs,
+            // "diagnostic items compiler internal support for linting",
+            // cfg_fn!(rustc_attrs),
+        // ),
     },
     gated!(
         // Used in resolve:
@@ -568,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
          for reserving for `for<T> From<!> for T` impl"
     ),
     rustc_attr!(
-        rustc_test_marker, Normal, template!(Word), WarnFollowing,
+        rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing,
         "the `#[rustc_test_marker]` attribute is used internally to track tests",
     ),
     rustc_attr!(
@@ -594,11 +627,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         definition of a trait, it's currently in experimental form and should be changed before \
         being exposed outside of the std"
     ),
+    rustc_attr!(
+        rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
+        r#"`rustc_doc_primitive` is a rustc internal attribute"#,
+    ),
 
     // ==========================================================================
     // Internal attributes, Testing:
     // ==========================================================================
 
+    rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
@@ -639,6 +677,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk),
+    gated!(
+        custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#),
+        ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite",
+    ),
     rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
new file mode 100644
index 00000000000..e4c8d446af7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
@@ -0,0 +1,40 @@
+//! This module contains tests for doc-expression parsing.
+//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
+
+use mbe::syntax_node_to_token_tree;
+use syntax::{ast, AstNode};
+
+use crate::attr::{DocAtom, DocExpr};
+
+fn assert_parse_result(input: &str, expected: DocExpr) {
+    let (tt, _) = {
+        let source_file = ast::SourceFile::parse(input).ok().unwrap();
+        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+        syntax_node_to_token_tree(tt.syntax())
+    };
+    let cfg = DocExpr::parse(&tt);
+    assert_eq!(cfg, expected);
+}
+
+#[test]
+fn test_doc_expr_parser() {
+    assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
+
+    assert_parse_result(
+        r#"#![doc(alias = "foo")]"#,
+        DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
+    );
+
+    assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
+    assert_parse_result(
+        r#"#![doc(alias("foo", "bar", "baz"))]"#,
+        DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
+    );
+
+    assert_parse_result(
+        r#"
+        #[doc(alias("Bar", "Qux"))]
+        struct Foo;"#,
+        DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index b70e658efd7..36626ed1a9b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -6,267 +6,30 @@ mod tests;
 pub mod scope;
 mod pretty;
 
-use std::{ops::Index, sync::Arc};
+use std::ops::Index;
 
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
-use drop_bomb::DropBomb;
 use either::Either;
-use hir_expand::{
-    attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
-};
+use hir_expand::{name::Name, HirFileId, InFile};
 use la_arena::{Arena, ArenaMap};
-use limit::Limit;
 use profile::Count;
 use rustc_hash::FxHashMap;
-use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
+use syntax::{ast, AstPtr, SyntaxNodePtr};
+use triomphe::Arc;
 
 use crate::{
-    attr::Attrs,
     db::DefDatabase,
-    expr::{
+    expander::Expander,
+    hir::{
         dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
     },
-    item_scope::BuiltinShadowMode,
-    macro_id_to_def_id,
     nameres::DefMap,
     path::{ModPath, Path},
     src::{HasChildSource, HasSource},
-    AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
-    UnresolvedMacro,
+    BlockId, DefWithBodyId, HasModule, Lookup,
 };
 
-pub use lower::LowerCtx;
-
-/// A subset of Expander that only deals with cfg attributes. We only need it to
-/// avoid cyclic queries in crate def map during enum processing.
-#[derive(Debug)]
-pub(crate) struct CfgExpander {
-    cfg_options: CfgOptions,
-    hygiene: Hygiene,
-    krate: CrateId,
-}
-
-#[derive(Debug)]
-pub struct Expander {
-    cfg_expander: CfgExpander,
-    def_map: Arc<DefMap>,
-    current_file_id: HirFileId,
-    module: LocalModuleId,
-    /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
-    recursion_depth: usize,
-}
-
-impl CfgExpander {
-    pub(crate) fn new(
-        db: &dyn DefDatabase,
-        current_file_id: HirFileId,
-        krate: CrateId,
-    ) -> CfgExpander {
-        let hygiene = Hygiene::new(db.upcast(), current_file_id);
-        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
-        CfgExpander { cfg_options, hygiene, krate }
-    }
-
-    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
-        Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
-    }
-
-    pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
-        let attrs = self.parse_attrs(db, owner);
-        attrs.is_cfg_enabled(&self.cfg_options)
-    }
-}
-
-impl Expander {
-    pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
-        let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
-        let def_map = module.def_map(db);
-        Expander {
-            cfg_expander,
-            def_map,
-            current_file_id,
-            module: module.local_id,
-            recursion_depth: 0,
-        }
-    }
-
-    pub fn enter_expand<T: ast::AstNode>(
-        &mut self,
-        db: &dyn DefDatabase,
-        macro_call: ast::MacroCall,
-    ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
-        let mut unresolved_macro_err = None;
-
-        let result = self.within_limit(db, |this| {
-            let macro_call = InFile::new(this.current_file_id, &macro_call);
-
-            let resolver =
-                |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
-
-            let mut err = None;
-            let call_id = match macro_call.as_call_id_with_errors(
-                db,
-                this.def_map.krate(),
-                resolver,
-                &mut |e| {
-                    err.get_or_insert(e);
-                },
-            ) {
-                Ok(call_id) => call_id,
-                Err(resolve_err) => {
-                    unresolved_macro_err = Some(resolve_err);
-                    return ExpandResult { value: None, err: None };
-                }
-            };
-            ExpandResult { value: call_id.ok(), err }
-        });
-
-        if let Some(err) = unresolved_macro_err {
-            Err(err)
-        } else {
-            Ok(result)
-        }
-    }
-
-    pub fn enter_expand_id<T: ast::AstNode>(
-        &mut self,
-        db: &dyn DefDatabase,
-        call_id: MacroCallId,
-    ) -> ExpandResult<Option<(Mark, T)>> {
-        self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
-    }
-
-    fn enter_expand_inner(
-        db: &dyn DefDatabase,
-        call_id: MacroCallId,
-        mut err: Option<ExpandError>,
-    ) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> {
-        if err.is_none() {
-            err = db.macro_expand_error(call_id);
-        }
-
-        let file_id = call_id.as_file();
-
-        let raw_node = match db.parse_or_expand(file_id) {
-            Some(it) => it,
-            None => {
-                // Only `None` if the macro expansion produced no usable AST.
-                if err.is_none() {
-                    tracing::warn!("no error despite `parse_or_expand` failing");
-                }
-
-                return ExpandResult::only_err(err.unwrap_or_else(|| {
-                    ExpandError::Other("failed to parse macro invocation".into())
-                }));
-            }
-        };
-
-        ExpandResult { value: Some((file_id, raw_node)), err }
-    }
-
-    pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
-        self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
-        self.current_file_id = mark.file_id;
-        if self.recursion_depth == usize::MAX {
-            // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
-            // depth only when we get out of the tree.
-            if !self.current_file_id.is_macro() {
-                self.recursion_depth = 0;
-            }
-        } else {
-            self.recursion_depth -= 1;
-        }
-        mark.bomb.defuse();
-    }
-
-    pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
-        InFile { file_id: self.current_file_id, value }
-    }
-
-    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
-        self.cfg_expander.parse_attrs(db, owner)
-    }
-
-    pub(crate) fn cfg_options(&self) -> &CfgOptions {
-        &self.cfg_expander.cfg_options
-    }
-
-    pub fn current_file_id(&self) -> HirFileId {
-        self.current_file_id
-    }
-
-    fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
-        let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
-        Path::from_src(path, &ctx)
-    }
-
-    fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
-        self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros()
-    }
-
-    fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
-        let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
-
-        #[cfg(not(test))]
-        return Limit::new(limit);
-
-        // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
-        #[cfg(test)]
-        return Limit::new(std::cmp::min(32, limit));
-    }
-
-    fn within_limit<F, T: ast::AstNode>(
-        &mut self,
-        db: &dyn DefDatabase,
-        op: F,
-    ) -> ExpandResult<Option<(Mark, T)>>
-    where
-        F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
-    {
-        if self.recursion_depth == usize::MAX {
-            // Recursion limit has been reached somewhere in the macro expansion tree. We should
-            // stop expanding other macro calls in this tree, or else this may result in
-            // exponential number of macro expansions, leading to a hang.
-            //
-            // The overflow error should have been reported when it occurred (see the next branch),
-            // so don't return overflow error here to avoid diagnostics duplication.
-            cov_mark::hit!(overflow_but_not_me);
-            return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
-        } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
-            self.recursion_depth = usize::MAX;
-            cov_mark::hit!(your_stack_belongs_to_me);
-            return ExpandResult::only_err(ExpandError::Other(
-                "reached recursion limit during macro expansion".into(),
-            ));
-        }
-
-        let ExpandResult { value, err } = op(self);
-        let Some(call_id) = value else {
-            return ExpandResult { value: None, err };
-        };
-
-        Self::enter_expand_inner(db, call_id, err).map(|value| {
-            value.and_then(|(new_file_id, node)| {
-                let node = T::cast(node)?;
-
-                self.recursion_depth += 1;
-                self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id);
-                let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id);
-                let mark =
-                    Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
-                Some((mark, node))
-            })
-        })
-    }
-}
-
-#[derive(Debug)]
-pub struct Mark {
-    file_id: HirFileId,
-    bomb: DropBomb,
-}
-
 /// The body of an item (function, const etc.).
 #[derive(Debug, Eq, PartialEq)]
 pub struct Body {
@@ -343,6 +106,8 @@ pub enum BodyDiagnostic {
     MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
     UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
     UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+    UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
+    UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
 }
 
 impl Body {
@@ -353,45 +118,54 @@ impl Body {
         let _p = profile::span("body_with_source_map_query");
         let mut params = None;
 
-        let (file_id, module, body) = match def {
-            DefWithBodyId::FunctionId(f) => {
-                let f = f.lookup(db);
-                let src = f.source(db);
-                params = src.value.param_list().map(|param_list| {
-                    let item_tree = f.id.item_tree(db);
-                    let func = &item_tree[f.id.value];
-                    let krate = f.container.module(db).krate;
-                    let crate_graph = db.crate_graph();
+        let (file_id, module, body, is_async_fn) = {
+            match def {
+                DefWithBodyId::FunctionId(f) => {
+                    let data = db.function_data(f);
+                    let f = f.lookup(db);
+                    let src = f.source(db);
+                    params = src.value.param_list().map(|param_list| {
+                        let item_tree = f.id.item_tree(db);
+                        let func = &item_tree[f.id.value];
+                        let krate = f.container.module(db).krate;
+                        let crate_graph = db.crate_graph();
+                        (
+                            param_list,
+                            func.params.clone().map(move |param| {
+                                item_tree
+                                    .attrs(db, krate, param.into())
+                                    .is_cfg_enabled(&crate_graph[krate].cfg_options)
+                            }),
+                        )
+                    });
                     (
-                        param_list,
-                        func.params.clone().map(move |param| {
-                            item_tree
-                                .attrs(db, krate, param.into())
-                                .is_cfg_enabled(&crate_graph[krate].cfg_options)
-                        }),
+                        src.file_id,
+                        f.module(db),
+                        src.value.body().map(ast::Expr::from),
+                        data.has_async_kw(),
                     )
-                });
-                (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
-            }
-            DefWithBodyId::ConstId(c) => {
-                let c = c.lookup(db);
-                let src = c.source(db);
-                (src.file_id, c.module(db), src.value.body())
-            }
-            DefWithBodyId::StaticId(s) => {
-                let s = s.lookup(db);
-                let src = s.source(db);
-                (src.file_id, s.module(db), src.value.body())
-            }
-            DefWithBodyId::VariantId(v) => {
-                let e = v.parent.lookup(db);
-                let src = v.parent.child_source(db);
-                let variant = &src.value[v.local_id];
-                (src.file_id, e.container, variant.expr())
+                }
+                DefWithBodyId::ConstId(c) => {
+                    let c = c.lookup(db);
+                    let src = c.source(db);
+                    (src.file_id, c.module(db), src.value.body(), false)
+                }
+                DefWithBodyId::StaticId(s) => {
+                    let s = s.lookup(db);
+                    let src = s.source(db);
+                    (src.file_id, s.module(db), src.value.body(), false)
+                }
+                DefWithBodyId::VariantId(v) => {
+                    let e = v.parent.lookup(db);
+                    let src = v.parent.child_source(db);
+                    let variant = &src.value[v.local_id];
+                    (src.file_id, e.container, variant.expr(), false)
+                }
             }
         };
         let expander = Expander::new(db, file_id, module);
-        let (mut body, source_map) = Body::new(db, expander, params, body);
+        let (mut body, source_map) =
+            Body::new(db, def, expander, params, body, module.krate, is_async_fn);
         body.shrink_to_fit();
 
         (Arc::new(body), Arc::new(source_map))
@@ -406,22 +180,32 @@ impl Body {
         &'a self,
         db: &'a dyn DefDatabase,
     ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
-        self.block_scopes
-            .iter()
-            .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
+        self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
     }
 
     pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
         pretty::print_body_hir(db, self, owner)
     }
 
+    pub fn pretty_print_expr(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        expr: ExprId,
+    ) -> String {
+        pretty::print_expr_hir(db, self, owner, expr)
+    }
+
     fn new(
         db: &dyn DefDatabase,
+        owner: DefWithBodyId,
         expander: Expander,
         params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
         body: Option<ast::Expr>,
+        krate: CrateId,
+        is_async_fn: bool,
     ) -> (Body, BodySourceMap) {
-        lower::lower(db, expander, params, body)
+        lower::lower(db, owner, expander, params, body, krate, is_async_fn)
     }
 
     fn shrink_to_fit(&mut self) {
@@ -437,15 +221,14 @@ impl Body {
 
     pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
         self.walk_pats(pat_id, &mut |pat| {
-            if let Pat::Bind { id, .. } = pat {
+            if let Pat::Bind { id, .. } = &self[pat] {
                 f(*id);
             }
         });
     }
 
-    pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
+    pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
         let pat = &self[pat_id];
-        f(pat);
         match pat {
             Pat::Range { .. }
             | Pat::Lit(..)
@@ -455,23 +238,28 @@ impl Body {
             | Pat::Missing => {}
             &Pat::Bind { subpat, .. } => {
                 if let Some(subpat) = subpat {
-                    self.walk_pats(subpat, f);
+                    f(subpat);
                 }
             }
             Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
-                args.iter().copied().for_each(|p| self.walk_pats(p, f));
+                args.iter().copied().for_each(|p| f(p));
             }
-            Pat::Ref { pat, .. } => self.walk_pats(*pat, f),
+            Pat::Ref { pat, .. } => f(*pat),
             Pat::Slice { prefix, slice, suffix } => {
                 let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
-                total_iter.copied().for_each(|p| self.walk_pats(p, f));
+                total_iter.copied().for_each(|p| f(p));
             }
             Pat::Record { args, .. } => {
-                args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f));
+                args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
             }
-            Pat::Box { inner } => self.walk_pats(*inner, f),
+            Pat::Box { inner } => f(*inner),
         }
     }
+
+    pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
+        f(pat_id);
+        self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
+    }
 }
 
 impl Default for Body {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index fedaf395598..7b88e525bf1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -1,88 +1,62 @@
 //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
 //! representation.
 
-use std::{mem, sync::Arc};
+use std::mem;
 
+use base_db::CrateId;
 use either::Either;
 use hir_expand::{
     ast_id_map::AstIdMap,
-    hygiene::Hygiene,
     name::{name, AsName, Name},
-    AstId, ExpandError, HirFileId, InFile,
+    AstId, ExpandError, InFile,
 };
 use intern::Interned;
 use la_arena::Arena;
-use once_cell::unsync::OnceCell;
 use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use syntax::{
     ast::{
-        self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
+        self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
         SlicePatComponents,
     },
     AstNode, AstPtr, SyntaxNodePtr,
 };
+use triomphe::Arc;
 
 use crate::{
-    adt::StructKind,
-    body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
-    body::{BodyDiagnostic, ExprSource, PatSource},
-    builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
+    body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+    data::adt::StructKind,
     db::DefDatabase,
-    expr::{
-        dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
-        FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
-        RecordFieldPat, RecordLitField, Statement,
+    expander::Expander,
+    hir::{
+        dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
+        ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+        Pat, PatId, RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
+    lang_item::LangItem,
+    lower::LowerCtx,
+    nameres::{DefMap, MacroSubNs},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
+    AdtId, BlockId, BlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
 };
 
-pub struct LowerCtx<'a> {
-    pub db: &'a dyn DefDatabase,
-    hygiene: Hygiene,
-    ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
-}
-
-impl<'a> LowerCtx<'a> {
-    pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
-        LowerCtx {
-            db,
-            hygiene: Hygiene::new(db.upcast(), file_id),
-            ast_id_map: Some((file_id, OnceCell::new())),
-        }
-    }
-
-    pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
-        LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
-    }
-
-    pub(crate) fn hygiene(&self) -> &Hygiene {
-        &self.hygiene
-    }
-
-    pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
-        Path::from_src(ast, self)
-    }
-
-    pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
-        let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
-        let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
-        Some(InFile::new(file_id, ast_id_map.ast_id(item)))
-    }
-}
-
 pub(super) fn lower(
     db: &dyn DefDatabase,
+    owner: DefWithBodyId,
     expander: Expander,
     params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
     body: Option<ast::Expr>,
+    krate: CrateId,
+    is_async_fn: bool,
 ) -> (Body, BodySourceMap) {
     ExprCollector {
         db,
+        owner,
+        krate,
+        def_map: expander.module.def_map(db),
         source_map: BodySourceMap::default(),
         ast_id_map: db.ast_id_map(expander.current_file_id),
         body: Body {
@@ -96,25 +70,79 @@ pub(super) fn lower(
             _c: Count::new(),
         },
         expander,
+        current_try_block_label: None,
         is_lowering_assignee_expr: false,
         is_lowering_generator: false,
+        label_ribs: Vec::new(),
+        current_binding_owner: None,
     }
-    .collect(params, body)
+    .collect(params, body, is_async_fn)
 }
 
 struct ExprCollector<'a> {
     db: &'a dyn DefDatabase,
     expander: Expander,
+    owner: DefWithBodyId,
+    def_map: Arc<DefMap>,
     ast_id_map: Arc<AstIdMap>,
+    krate: CrateId,
     body: Body,
     source_map: BodySourceMap,
+
     is_lowering_assignee_expr: bool,
     is_lowering_generator: bool,
+
+    current_try_block_label: Option<LabelId>,
+    // points to the expression that a try expression will target (replaces current_try_block_label)
+    // catch_scope: Option<ExprId>,
+    // points to the expression that an unlabeled control flow will target
+    // loop_scope: Option<ExprId>,
+    // needed to diagnose non label control flow in while conditions
+    // is_in_loop_condition: bool,
+
+    // resolution
+    label_ribs: Vec<LabelRib>,
+    current_binding_owner: Option<ExprId>,
+}
+
+#[derive(Clone, Debug)]
+struct LabelRib {
+    kind: RibKind,
+    // Once we handle macro hygiene this will need to be a map
+    label: Option<(Name, LabelId)>,
+}
+
+impl LabelRib {
+    fn new(kind: RibKind) -> Self {
+        LabelRib { kind, label: None }
+    }
+    fn new_normal(label: (Name, LabelId)) -> Self {
+        LabelRib { kind: RibKind::Normal, label: Some(label) }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum RibKind {
+    Normal,
+    Closure,
+    Constant,
+}
+
+impl RibKind {
+    /// This rib forbids referring to labels defined in upwards ribs.
+    fn is_label_barrier(self) -> bool {
+        match self {
+            RibKind::Normal => false,
+            RibKind::Closure | RibKind::Constant => true,
+        }
+    }
 }
 
 #[derive(Debug, Default)]
 struct BindingList {
     map: FxHashMap<Name, BindingId>,
+    is_used: FxHashMap<BindingId, bool>,
+    reject_new: bool,
 }
 
 impl BindingList {
@@ -124,7 +152,27 @@ impl BindingList {
         name: Name,
         mode: BindingAnnotation,
     ) -> BindingId {
-        *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode))
+        let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode));
+        if ec.body.bindings[id].mode != mode {
+            ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
+        }
+        self.check_is_used(ec, id);
+        id
+    }
+
+    fn check_is_used(&mut self, ec: &mut ExprCollector<'_>, id: BindingId) {
+        match self.is_used.get(&id) {
+            None => {
+                if self.reject_new {
+                    ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
+                }
+            }
+            Some(true) => {
+                ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
+            }
+            Some(false) => {}
+        }
+        self.is_used.insert(id, true);
     }
 }
 
@@ -133,6 +181,7 @@ impl ExprCollector<'_> {
         mut self,
         param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
         body: Option<ast::Expr>,
+        is_async_fn: bool,
     ) -> (Body, BodySourceMap) {
         if let Some((param_list, mut attr_enabled)) = param_list {
             if let Some(self_param) =
@@ -152,72 +201,35 @@ impl ExprCollector<'_> {
                 self.body.params.push(param_pat);
             }
 
-            for pat in param_list
-                .params()
-                .zip(attr_enabled)
-                .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+            for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
             {
-                let param_pat = self.collect_pat(pat);
+                let param_pat = self.collect_pat_top(param.pat());
                 self.body.params.push(param_pat);
             }
         };
+        self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
+            if is_async_fn {
+                match body {
+                    Some(e) => {
+                        let expr = this.collect_expr(e);
+                        this.alloc_expr_desugared(Expr::Async {
+                            id: None,
+                            statements: Box::new([]),
+                            tail: Some(expr),
+                        })
+                    }
+                    None => this.missing_expr(),
+                }
+            } else {
+                this.collect_expr_opt(body)
+            }
+        });
 
-        self.body.body_expr = self.collect_expr_opt(body);
         (self.body, self.source_map)
     }
 
     fn ctx(&self) -> LowerCtx<'_> {
-        LowerCtx::new(self.db, self.expander.current_file_id)
-    }
-
-    fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
-        let src = self.expander.to_source(ptr);
-        let id = self.make_expr(expr, src.clone());
-        self.source_map.expr_map.insert(src, id);
-        id
-    }
-    // desugared exprs don't have ptr, that's wrong and should be fixed
-    // somehow.
-    fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
-        self.body.exprs.alloc(expr)
-    }
-    fn missing_expr(&mut self) -> ExprId {
-        self.alloc_expr_desugared(Expr::Missing)
-    }
-    fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
-        let id = self.body.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src);
-        id
-    }
-
-    fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
-        self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
-    }
-    fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
-        let src = self.expander.to_source(ptr);
-        let id = self.make_pat(pat, src.clone());
-        self.source_map.pat_map.insert(src, id);
-        id
-    }
-    fn missing_pat(&mut self) -> PatId {
-        self.body.pats.alloc(Pat::Missing)
-    }
-    fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
-        let id = self.body.pats.alloc(pat);
-        self.source_map.pat_map_back.insert(id, src);
-        id
-    }
-
-    fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
-        let src = self.expander.to_source(ptr);
-        let id = self.make_label(label, src.clone());
-        self.source_map.label_map.insert(src, id);
-        id
-    }
-    fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
-        let id = self.body.labels.alloc(label);
-        self.source_map.label_map_back.insert(id, src);
-        id
+        self.expander.ctx(self.db)
     }
 
     fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -229,6 +241,7 @@ impl ExprCollector<'_> {
         let syntax_ptr = AstPtr::new(&expr);
         self.check_cfg(&expr)?;
 
+        // FIXME: Move some of these arms out into separate methods for clarity
         Some(match expr {
             ast::Expr::IfExpr(e) => {
                 let then_branch = self.collect_block_opt(e.then_branch());
@@ -246,18 +259,12 @@ impl ExprCollector<'_> {
                 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
             }
             ast::Expr::LetExpr(e) => {
-                let pat = self.collect_pat_opt(e.pat());
+                let pat = self.collect_pat_top(e.pat());
                 let expr = self.collect_expr_opt(e.expr());
                 self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
             }
             ast::Expr::BlockExpr(e) => match e.modifier() {
-                Some(ast::BlockModifier::Try(_)) => {
-                    self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
-                        id,
-                        statements,
-                        tail,
-                    })
-                }
+                Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
                 Some(ast::BlockModifier::Unsafe(_)) => {
                     self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
                         id,
@@ -267,50 +274,74 @@ impl ExprCollector<'_> {
                 }
                 Some(ast::BlockModifier::Label(label)) => {
                     let label = self.collect_label(label);
-                    self.collect_block_(e, |id, statements, tail| Expr::Block {
-                        id,
-                        statements,
-                        tail,
-                        label: Some(label),
+                    self.with_labeled_rib(label, |this| {
+                        this.collect_block_(e, |id, statements, tail| Expr::Block {
+                            id,
+                            statements,
+                            tail,
+                            label: Some(label),
+                        })
+                    })
+                }
+                Some(ast::BlockModifier::Async(_)) => {
+                    self.with_label_rib(RibKind::Closure, |this| {
+                        this.collect_block_(e, |id, statements, tail| Expr::Async {
+                            id,
+                            statements,
+                            tail,
+                        })
+                    })
+                }
+                Some(ast::BlockModifier::Const(_)) => {
+                    self.with_label_rib(RibKind::Constant, |this| {
+                        let (result_expr_id, prev_binding_owner) =
+                            this.initialize_binding_owner(syntax_ptr);
+                        let inner_expr = this.collect_block(e);
+                        let x = this.db.intern_anonymous_const((this.owner, inner_expr));
+                        this.body.exprs[result_expr_id] = Expr::Const(x);
+                        this.current_binding_owner = prev_binding_owner;
+                        result_expr_id
                     })
                 }
-                Some(ast::BlockModifier::Async(_)) => self
-                    .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
-                Some(ast::BlockModifier::Const(_)) => self
-                    .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
                 None => self.collect_block(e),
             },
             ast::Expr::LoopExpr(e) => {
                 let label = e.label().map(|label| self.collect_label(label));
-                let body = self.collect_block_opt(e.loop_body());
+                let body = self.collect_labelled_block_opt(label, e.loop_body());
                 self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
             }
             ast::Expr::WhileExpr(e) => {
                 let label = e.label().map(|label| self.collect_label(label));
-                let body = self.collect_block_opt(e.loop_body());
-
+                let body = self.collect_labelled_block_opt(label, e.loop_body());
                 let condition = self.collect_expr_opt(e.condition());
 
                 self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
             }
-            ast::Expr::ForExpr(e) => {
-                let label = e.label().map(|label| self.collect_label(label));
-                let iterable = self.collect_expr_opt(e.iterable());
-                let pat = self.collect_pat_opt(e.pat());
-                let body = self.collect_block_opt(e.loop_body());
-                self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
-            }
+            ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
             ast::Expr::CallExpr(e) => {
-                let callee = self.collect_expr_opt(e.expr());
-                let args = if let Some(arg_list) = e.arg_list() {
-                    arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
-                } else {
-                    Box::default()
+                let is_rustc_box = {
+                    let attrs = e.attrs();
+                    attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
                 };
-                self.alloc_expr(
-                    Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
-                    syntax_ptr,
-                )
+                if is_rustc_box {
+                    let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
+                    self.alloc_expr(Expr::Box { expr }, syntax_ptr)
+                } else {
+                    let callee = self.collect_expr_opt(e.expr());
+                    let args = if let Some(arg_list) = e.arg_list() {
+                        arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
+                    } else {
+                        Box::default()
+                    };
+                    self.alloc_expr(
+                        Expr::Call {
+                            callee,
+                            args,
+                            is_assignee_expr: self.is_lowering_assignee_expr,
+                        },
+                        syntax_ptr,
+                    )
+                }
             }
             ast::Expr::MethodCallExpr(e) => {
                 let receiver = self.collect_expr_opt(e.receiver());
@@ -336,7 +367,7 @@ impl ExprCollector<'_> {
                         .arms()
                         .filter_map(|arm| {
                             self.check_cfg(&arm).map(|()| MatchArm {
-                                pat: self.collect_pat_opt(arm.pat()),
+                                pat: self.collect_pat_top(arm.pat()),
                                 expr: self.collect_expr_opt(arm.expr()),
                                 guard: arm
                                     .guard()
@@ -357,16 +388,20 @@ impl ExprCollector<'_> {
                     .unwrap_or(Expr::Missing);
                 self.alloc_expr(path, syntax_ptr)
             }
-            ast::Expr::ContinueExpr(e) => self.alloc_expr(
-                Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
-                syntax_ptr,
-            ),
+            ast::Expr::ContinueExpr(e) => {
+                let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+                    self.source_map.diagnostics.push(e);
+                    None
+                });
+                self.alloc_expr(Expr::Continue { label }, syntax_ptr)
+            }
             ast::Expr::BreakExpr(e) => {
+                let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+                    self.source_map.diagnostics.push(e);
+                    None
+                });
                 let expr = e.expr().map(|e| self.collect_expr(e));
-                self.alloc_expr(
-                    Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
-                    syntax_ptr,
-                )
+                self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
             }
             ast::Expr::ParenExpr(e) => {
                 let inner = self.collect_expr_opt(e.expr());
@@ -437,10 +472,7 @@ impl ExprCollector<'_> {
                 let expr = self.collect_expr_opt(e.expr());
                 self.alloc_expr(Expr::Await { expr }, syntax_ptr)
             }
-            ast::Expr::TryExpr(e) => {
-                let expr = self.collect_expr_opt(e.expr());
-                self.alloc_expr(Expr::Try { expr }, syntax_ptr)
-            }
+            ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
             ast::Expr::CastExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
                 let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
@@ -470,14 +502,16 @@ impl ExprCollector<'_> {
                     None => self.alloc_expr(Expr::Missing, syntax_ptr),
                 }
             }
-            ast::Expr::ClosureExpr(e) => {
+            ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
+                let (result_expr_id, prev_binding_owner) =
+                    this.initialize_binding_owner(syntax_ptr);
                 let mut args = Vec::new();
                 let mut arg_types = Vec::new();
                 if let Some(pl) = e.param_list() {
                     for param in pl.params() {
-                        let pat = self.collect_pat_opt(param.pat());
+                        let pat = this.collect_pat_top(param.pat());
                         let type_ref =
-                            param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                            param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
                         args.push(pat);
                         arg_types.push(type_ref);
                     }
@@ -485,14 +519,14 @@ impl ExprCollector<'_> {
                 let ret_type = e
                     .ret_type()
                     .and_then(|r| r.ty())
-                    .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                    .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
 
-                let prev_is_lowering_generator = self.is_lowering_generator;
-                self.is_lowering_generator = false;
+                let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
+                let prev_try_block_label = this.current_try_block_label.take();
 
-                let body = self.collect_expr_opt(e.body());
+                let body = this.collect_expr_opt(e.body());
 
-                let closure_kind = if self.is_lowering_generator {
+                let closure_kind = if this.is_lowering_generator {
                     let movability = if e.static_token().is_some() {
                         Movability::Static
                     } else {
@@ -504,19 +538,21 @@ impl ExprCollector<'_> {
                 } else {
                     ClosureKind::Closure
                 };
-                self.is_lowering_generator = prev_is_lowering_generator;
-
-                self.alloc_expr(
-                    Expr::Closure {
-                        args: args.into(),
-                        arg_types: arg_types.into(),
-                        ret_type,
-                        body,
-                        closure_kind,
-                    },
-                    syntax_ptr,
-                )
-            }
+                let capture_by =
+                    if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+                this.is_lowering_generator = prev_is_lowering_generator;
+                this.current_binding_owner = prev_binding_owner;
+                this.current_try_block_label = prev_try_block_label;
+                this.body.exprs[result_expr_id] = Expr::Closure {
+                    args: args.into(),
+                    arg_types: arg_types.into(),
+                    ret_type,
+                    body,
+                    closure_kind,
+                    capture_by,
+                };
+                result_expr_id
+            }),
             ast::Expr::BinExpr(e) => {
                 let op = e.op_kind();
                 if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@@ -528,9 +564,18 @@ impl ExprCollector<'_> {
                 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
             }
             ast::Expr::TupleExpr(e) => {
-                let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
+                let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
+                // if there is a leading comma, the user is most likely to type out a leading expression
+                // so we insert a missing expression at the beginning for IDE features
+                if comma_follows_token(e.l_paren_token()) {
+                    exprs.insert(0, self.missing_expr());
+                }
+
                 self.alloc_expr(
-                    Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
+                    Expr::Tuple {
+                        exprs: exprs.into_boxed_slice(),
+                        is_assignee_expr: self.is_lowering_assignee_expr,
+                    },
                     syntax_ptr,
                 )
             }
@@ -555,7 +600,17 @@ impl ExprCollector<'_> {
                     }
                     ArrayExprKind::Repeat { initializer, repeat } => {
                         let initializer = self.collect_expr_opt(initializer);
-                        let repeat = self.collect_expr_opt(repeat);
+                        let repeat = self.with_label_rib(RibKind::Constant, |this| {
+                            if let Some(repeat) = repeat {
+                                let syntax_ptr = AstPtr::new(&repeat);
+                                this.collect_as_a_binding_owner_bad(
+                                    |this| this.collect_expr(repeat),
+                                    syntax_ptr,
+                                )
+                            } else {
+                                this.missing_expr()
+                            }
+                        });
                         self.alloc_expr(
                             Expr::Array(Array::Repeat { initializer, repeat }),
                             syntax_ptr,
@@ -601,6 +656,244 @@ impl ExprCollector<'_> {
         })
     }
 
+    fn initialize_binding_owner(
+        &mut self,
+        syntax_ptr: AstPtr<ast::Expr>,
+    ) -> (ExprId, Option<ExprId>) {
+        let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
+        let prev_binding_owner = self.current_binding_owner.take();
+        self.current_binding_owner = Some(result_expr_id);
+        (result_expr_id, prev_binding_owner)
+    }
+
+    /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
+    /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
+    /// their own body. Don't add more usage for this function so that we can remove this function after
+    /// separating those bodies.
+    fn collect_as_a_binding_owner_bad(
+        &mut self,
+        job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
+        syntax_ptr: AstPtr<ast::Expr>,
+    ) -> ExprId {
+        let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
+        let tmp = job(self);
+        self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+        self.current_binding_owner = prev_owner;
+        id
+    }
+
+    /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
+    /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
+    /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
+    fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
+        let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
+            return self.collect_block(e);
+        };
+        let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
+        let old_label = self.current_try_block_label.replace(label);
+
+        let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+            let mut btail = None;
+            let block = this.collect_block_(e, |id, statements, tail| {
+                btail = tail;
+                Expr::Block { id, statements, tail, label: Some(label) }
+            });
+            (btail, block)
+        });
+
+        let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
+        let next_tail = match btail {
+            Some(tail) => self.alloc_expr_desugared(Expr::Call {
+                callee,
+                args: Box::new([tail]),
+                is_assignee_expr: false,
+            }),
+            None => {
+                let unit = self.alloc_expr_desugared(Expr::Tuple {
+                    exprs: Box::new([]),
+                    is_assignee_expr: false,
+                });
+                self.alloc_expr_desugared(Expr::Call {
+                    callee,
+                    args: Box::new([unit]),
+                    is_assignee_expr: false,
+                })
+            }
+        };
+        let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
+            unreachable!("block was lowered to non-block");
+        };
+        *tail = Some(next_tail);
+        self.current_try_block_label = old_label;
+        expr_id
+    }
+
+    /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
+    /// ```ignore (pseudo-rust)
+    /// match IntoIterator::into_iter(<head>) {
+    ///     mut iter => {
+    ///         [opt_ident]: loop {
+    ///             match Iterator::next(&mut iter) {
+    ///                 None => break,
+    ///                 Some(<pat>) => <body>,
+    ///             };
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
+        let (into_iter_fn, iter_next_fn, option_some, option_none) = 'if_chain: {
+            if let Some(into_iter_fn) = LangItem::IntoIterIntoIter.path(self.db, self.krate) {
+                if let Some(iter_next_fn) = LangItem::IteratorNext.path(self.db, self.krate) {
+                    if let Some(option_some) = LangItem::OptionSome.path(self.db, self.krate) {
+                        if let Some(option_none) = LangItem::OptionNone.path(self.db, self.krate) {
+                            break 'if_chain (into_iter_fn, iter_next_fn, option_some, option_none);
+                        }
+                    }
+                }
+            }
+            // Some of the needed lang items are missing, so we can't desugar
+            return self.alloc_expr(Expr::Missing, syntax_ptr);
+        };
+        let head = self.collect_expr_opt(e.iterable());
+        let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone());
+        let iterator = self.alloc_expr(
+            Expr::Call {
+                callee: into_iter_fn_expr,
+                args: Box::new([head]),
+                is_assignee_expr: false,
+            },
+            syntax_ptr.clone(),
+        );
+        let none_arm = MatchArm {
+            pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
+            guard: None,
+            expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()),
+        };
+        let some_pat = Pat::TupleStruct {
+            path: Some(Box::new(option_some)),
+            args: Box::new([self.collect_pat_top(e.pat())]),
+            ellipsis: None,
+        };
+        let label = e.label().map(|label| self.collect_label(label));
+        let some_arm = MatchArm {
+            pat: self.alloc_pat_desugared(some_pat),
+            guard: None,
+            expr: self.with_opt_labeled_rib(label, |this| {
+                this.collect_expr_opt(e.loop_body().map(|x| x.into()))
+            }),
+        };
+        let iter_name = Name::generate_new_name();
+        let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable);
+        let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name)), syntax_ptr.clone());
+        let iter_expr_mut = self.alloc_expr(
+            Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
+            syntax_ptr.clone(),
+        );
+        let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone());
+        let iter_next_expr = self.alloc_expr(
+            Expr::Call {
+                callee: iter_next_fn_expr,
+                args: Box::new([iter_expr_mut]),
+                is_assignee_expr: false,
+            },
+            syntax_ptr.clone(),
+        );
+        let loop_inner = self.alloc_expr(
+            Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) },
+            syntax_ptr.clone(),
+        );
+        let loop_outer =
+            self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone());
+        let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
+        self.alloc_expr(
+            Expr::Match {
+                expr: iterator,
+                arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]),
+            },
+            syntax_ptr.clone(),
+        )
+    }
+
+    /// Desugar `ast::TryExpr` from: `<expr>?` into:
+    /// ```ignore (pseudo-rust)
+    /// match Try::branch(<expr>) {
+    ///     ControlFlow::Continue(val) => val,
+    ///     ControlFlow::Break(residual) =>
+    ///         // If there is an enclosing `try {...}`:
+    ///         break 'catch_target Try::from_residual(residual),
+    ///         // Otherwise:
+    ///         return Try::from_residual(residual),
+    /// }
+    /// ```
+    fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
+        let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: {
+            if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) {
+                if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) {
+                    if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) {
+                        if let Some(try_from_residual) =
+                            LangItem::TryTraitFromResidual.path(self.db, self.krate)
+                        {
+                            break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual);
+                        }
+                    }
+                }
+            }
+            // Some of the needed lang items are missing, so we can't desugar
+            return self.alloc_expr(Expr::Missing, syntax_ptr);
+        };
+        let operand = self.collect_expr_opt(e.expr());
+        let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
+        let expr = self.alloc_expr(
+            Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
+            syntax_ptr.clone(),
+        );
+        let continue_name = Name::generate_new_name();
+        let continue_binding =
+            self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
+        let continue_bpat =
+            self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
+        self.add_definition_to_binding(continue_binding, continue_bpat);
+        let continue_arm = MatchArm {
+            pat: self.alloc_pat_desugared(Pat::TupleStruct {
+                path: Some(Box::new(cf_continue)),
+                args: Box::new([continue_bpat]),
+                ellipsis: None,
+            }),
+            guard: None,
+            expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()),
+        };
+        let break_name = Name::generate_new_name();
+        let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
+        let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
+        self.add_definition_to_binding(break_binding, break_bpat);
+        let break_arm = MatchArm {
+            pat: self.alloc_pat_desugared(Pat::TupleStruct {
+                path: Some(Box::new(cf_break)),
+                args: Box::new([break_bpat]),
+                ellipsis: None,
+            }),
+            guard: None,
+            expr: {
+                let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone());
+                let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
+                let result = self.alloc_expr(
+                    Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
+                    syntax_ptr.clone(),
+                );
+                self.alloc_expr(
+                    match self.current_try_block_label {
+                        Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+                        None => Expr::Return { expr: Some(result) },
+                    },
+                    syntax_ptr.clone(),
+                )
+            },
+        };
+        let arms = Box::new([continue_arm, break_arm]);
+        self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
+    }
+
     fn collect_macro_call<F, T, U>(
         &mut self,
         mcall: ast::MacroCall,
@@ -616,7 +909,19 @@ impl ExprCollector<'_> {
         let outer_file = self.expander.current_file_id;
 
         let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall));
-        let res = self.expander.enter_expand(self.db, mcall);
+        let module = self.expander.module.local_id;
+        let res = self.expander.enter_expand(self.db, mcall, |path| {
+            self.def_map
+                .resolve_path(
+                    self.db,
+                    module,
+                    &path,
+                    crate::item_scope::BuiltinShadowMode::Other,
+                    Some(MacroSubNs::Bang),
+                )
+                .0
+                .take_macros()
+        });
 
         let res = match res {
             Ok(res) => res,
@@ -639,7 +944,7 @@ impl ExprCollector<'_> {
                         krate: *krate,
                     });
                 }
-                Some(ExpandError::RecursionOverflowPosioned) => {
+                Some(ExpandError::RecursionOverflowPoisoned) => {
                     // Recursion limit has been reached in the macro expansion tree, but not in
                     // this very macro call. Don't add diagnostics to avoid duplication.
                 }
@@ -663,7 +968,11 @@ impl ExprCollector<'_> {
                     self.db.ast_id_map(self.expander.current_file_id),
                 );
 
-                let id = collector(self, Some(expansion));
+                if record_diagnostics {
+                    // FIXME: Report parse errors here
+                }
+
+                let id = collector(self, Some(expansion.tree()));
                 self.ast_id_map = prev_ast_id_map;
                 self.expander.exit(self.db, mark);
                 id
@@ -720,7 +1029,7 @@ impl ExprCollector<'_> {
                 if self.check_cfg(&stmt).is_none() {
                     return;
                 }
-                let pat = self.collect_pat_opt(stmt.pat());
+                let pat = self.collect_pat_top(stmt.pat());
                 let type_ref =
                     stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
                 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@@ -763,22 +1072,36 @@ impl ExprCollector<'_> {
     fn collect_block_(
         &mut self,
         block: ast::BlockExpr,
-        mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+        mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
     ) -> ExprId {
-        let file_local_id = self.ast_id_map.ast_id(&block);
-        let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
-        let block_loc =
-            BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
-        let block_id = self.db.intern_block(block_loc);
-
-        let (module, def_map) = match self.db.block_def_map(block_id) {
-            Some(def_map) => {
-                self.body.block_scopes.push(block_id);
-                (def_map.root(), def_map)
-            }
-            None => (self.expander.module, self.expander.def_map.clone()),
+        let block_has_items = {
+            let statement_has_item = block.statements().any(|stmt| match stmt {
+                ast::Stmt::Item(_) => true,
+                // Macro calls can be both items and expressions. The syntax library always treats
+                // them as expressions here, so we undo that.
+                ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
+                _ => false,
+            });
+            statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+        };
+
+        let block_id = if block_has_items {
+            let file_local_id = self.ast_id_map.ast_id(&block);
+            let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
+            Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module }))
+        } else {
+            None
         };
-        let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
+
+        let (module, def_map) =
+            match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
+                Some((def_map, block_id)) => {
+                    self.body.block_scopes.push(block_id);
+                    (def_map.module_id(DefMap::ROOT), def_map)
+                }
+                None => (self.expander.module, self.def_map.clone()),
+            };
+        let prev_def_map = mem::replace(&mut self.def_map, def_map);
         let prev_local_module = mem::replace(&mut self.expander.module, module);
 
         let mut statements = Vec::new();
@@ -800,7 +1123,7 @@ impl ExprCollector<'_> {
         let expr_id = self
             .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
 
-        self.expander.def_map = prev_def_map;
+        self.def_map = prev_def_map;
         self.expander.module = prev_local_module;
         expr_id
     }
@@ -812,43 +1135,46 @@ impl ExprCollector<'_> {
         }
     }
 
-    fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
-        let label = Label {
-            name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
-        };
-        self.alloc_label(label, AstPtr::new(&ast_label))
+    fn collect_labelled_block_opt(
+        &mut self,
+        label: Option<LabelId>,
+        expr: Option<ast::BlockExpr>,
+    ) -> ExprId {
+        match label {
+            Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+            None => self.collect_block_opt(expr),
+        }
     }
 
-    fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
-        self.collect_pat_(pat, &mut BindingList::default())
-    }
+    // region: patterns
 
-    fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+    fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
         match pat {
-            Some(pat) => self.collect_pat(pat),
+            Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
             None => self.missing_pat(),
         }
     }
 
-    fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
+    fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
         let pattern = match &pat {
             ast::Pat::IdentPat(bp) => {
                 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
 
                 let annotation =
                     BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
-                let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+                let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
 
                 let is_simple_ident_pat =
                     annotation == BindingAnnotation::Unannotated && subpat.is_none();
                 let (binding, pattern) = if is_simple_ident_pat {
                     // This could also be a single-segment path pattern. To
                     // decide that, we need to try resolving the name.
-                    let (resolved, _) = self.expander.def_map.resolve_path(
+                    let (resolved, _) = self.def_map.resolve_path(
                         self.db,
-                        self.expander.module,
+                        self.expander.module.local_id,
                         &name.clone().into(),
                         BuiltinShadowMode::Other,
+                        None,
                     );
                     match resolved.take_values() {
                         Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
@@ -887,11 +1213,15 @@ impl ExprCollector<'_> {
             ast::Pat::TupleStructPat(p) => {
                 let path =
                     p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+                let (args, ellipsis) = self.collect_tuple_pat(
+                    p.fields(),
+                    comma_follows_token(p.l_paren_token()),
+                    binding_list,
+                );
                 Pat::TupleStruct { path, args, ellipsis }
             }
             ast::Pat::RefPat(p) => {
-                let pat = self.collect_pat_opt_(p.pat(), binding_list);
+                let pat = self.collect_pat_opt(p.pat(), binding_list);
                 let mutability = Mutability::from_mutable(p.mut_token().is_some());
                 Pat::Ref { pat, mutability }
             }
@@ -900,13 +1230,42 @@ impl ExprCollector<'_> {
                     p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
                 path.map(Pat::Path).unwrap_or(Pat::Missing)
             }
-            ast::Pat::OrPat(p) => {
-                let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
-                Pat::Or(pats)
+            ast::Pat::OrPat(p) => 'b: {
+                let prev_is_used = mem::take(&mut binding_list.is_used);
+                let prev_reject_new = mem::take(&mut binding_list.reject_new);
+                let mut pats = Vec::with_capacity(p.pats().count());
+                let mut it = p.pats();
+                let Some(first) = it.next() else {
+                    break 'b Pat::Or(Box::new([]));
+                };
+                pats.push(self.collect_pat(first, binding_list));
+                binding_list.reject_new = true;
+                for rest in it {
+                    for (_, x) in binding_list.is_used.iter_mut() {
+                        *x = false;
+                    }
+                    pats.push(self.collect_pat(rest, binding_list));
+                    for (&id, &x) in binding_list.is_used.iter() {
+                        if !x {
+                            self.body.bindings[id].problems =
+                                Some(BindingProblems::NotBoundAcrossAll);
+                        }
+                    }
+                }
+                binding_list.reject_new = prev_reject_new;
+                let current_is_used = mem::replace(&mut binding_list.is_used, prev_is_used);
+                for (id, _) in current_is_used.into_iter() {
+                    binding_list.check_is_used(self, id);
+                }
+                Pat::Or(pats.into())
             }
-            ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
+            ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
             ast::Pat::TuplePat(p) => {
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+                let (args, ellipsis) = self.collect_tuple_pat(
+                    p.fields(),
+                    comma_follows_token(p.l_paren_token()),
+                    binding_list,
+                );
                 Pat::Tuple { args, ellipsis }
             }
             ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -919,7 +1278,7 @@ impl ExprCollector<'_> {
                     .fields()
                     .filter_map(|f| {
                         let ast_pat = f.pat()?;
-                        let pat = self.collect_pat_(ast_pat, binding_list);
+                        let pat = self.collect_pat(ast_pat, binding_list);
                         let name = f.field_name()?.as_name();
                         Some(RecordFieldPat { name, pat })
                     })
@@ -938,26 +1297,18 @@ impl ExprCollector<'_> {
 
                 // FIXME properly handle `RestPat`
                 Pat::Slice {
-                    prefix: prefix
-                        .into_iter()
-                        .map(|p| self.collect_pat_(p, binding_list))
-                        .collect(),
-                    slice: slice.map(|p| self.collect_pat_(p, binding_list)),
-                    suffix: suffix
-                        .into_iter()
-                        .map(|p| self.collect_pat_(p, binding_list))
-                        .collect(),
+                    prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
+                    slice: slice.map(|p| self.collect_pat(p, binding_list)),
+                    suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
                 }
             }
-            ast::Pat::LiteralPat(lit) => {
-                if let Some(ast_lit) = lit.literal() {
-                    let expr = Expr::Literal(ast_lit.kind().into());
-                    let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
-                    let expr_id = self.alloc_expr(expr, expr_ptr);
-                    Pat::Lit(expr_id)
-                } else {
-                    Pat::Missing
-                }
+            #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
+            ast::Pat::LiteralPat(lit) => 'b: {
+                let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
+                let expr = Expr::Literal(hir_lit);
+                let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
+                let expr_id = self.alloc_expr(expr, expr_ptr);
+                Pat::Lit(expr_id)
             }
             ast::Pat::RestPat(_) => {
                 // `RestPat` requires special handling and should not be mapped
@@ -969,12 +1320,18 @@ impl ExprCollector<'_> {
                 Pat::Missing
             }
             ast::Pat::BoxPat(boxpat) => {
-                let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
+                let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
                 Pat::Box { inner }
             }
             ast::Pat::ConstBlockPat(const_block_pat) => {
-                if let Some(expr) = const_block_pat.block_expr() {
-                    let expr_id = self.collect_block(expr);
+                if let Some(block) = const_block_pat.block_expr() {
+                    let expr_id = self.with_label_rib(RibKind::Constant, |this| {
+                        let syntax_ptr = AstPtr::new(&block.clone().into());
+                        this.collect_as_a_binding_owner_bad(
+                            |this| this.collect_block(block),
+                            syntax_ptr,
+                        )
+                    });
                     Pat::ConstBlock(expr_id)
                 } else {
                     Pat::Missing
@@ -986,23 +1343,45 @@ impl ExprCollector<'_> {
                     let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
                     let pat =
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
-                            this.collect_pat_opt_(expanded_pat, binding_list)
+                            this.collect_pat_opt(expanded_pat, binding_list)
                         });
                     self.source_map.pat_map.insert(src, pat);
                     return pat;
                 }
                 None => Pat::Missing,
             },
-            // FIXME: implement
-            ast::Pat::RangePat(_) => Pat::Missing,
+            // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
+            ast::Pat::RangePat(p) => {
+                let mut range_part_lower = |p: Option<ast::Pat>| {
+                    p.and_then(|x| match &x {
+                        ast::Pat::LiteralPat(x) => {
+                            Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0)))
+                        }
+                        ast::Pat::IdentPat(p) => {
+                            let name =
+                                p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
+                            Some(Box::new(LiteralOrConst::Const(name.into())))
+                        }
+                        ast::Pat::PathPat(p) => p
+                            .path()
+                            .and_then(|path| self.expander.parse_path(self.db, path))
+                            .map(LiteralOrConst::Const)
+                            .map(Box::new),
+                        _ => None,
+                    })
+                };
+                let start = range_part_lower(p.start());
+                let end = range_part_lower(p.end());
+                Pat::Range { start, end }
+            }
         };
         let ptr = AstPtr::new(&pat);
         self.alloc_pat(pattern, Either::Left(ptr))
     }
 
-    fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
+    fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
         match pat {
-            Some(pat) => self.collect_pat_(pat, binding_list),
+            Some(pat) => self.collect_pat(pat, binding_list),
             None => self.missing_pat(),
         }
     }
@@ -1010,20 +1389,28 @@ impl ExprCollector<'_> {
     fn collect_tuple_pat(
         &mut self,
         args: AstChildren<ast::Pat>,
+        has_leading_comma: bool,
         binding_list: &mut BindingList,
     ) -> (Box<[PatId]>, Option<usize>) {
         // Find the location of the `..`, if there is one. Note that we do not
         // consider the possibility of there being multiple `..` here.
         let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
         // We want to skip the `..` pattern here, since we account for it above.
-        let args = args
+        let mut args: Vec<_> = args
             .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
-            .map(|p| self.collect_pat_(p, binding_list))
+            .map(|p| self.collect_pat(p, binding_list))
             .collect();
+        // if there is a leading comma, the user is most likely to type out a leading pattern
+        // so we insert a missing pattern at the beginning for IDE features
+        if has_leading_comma {
+            args.insert(0, self.missing_pat());
+        }
 
-        (args, ellipsis)
+        (args.into_boxed_slice(), ellipsis)
     }
 
+    // endregion: patterns
+
     /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
     /// not.
     fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@@ -1051,42 +1438,147 @@ impl ExprCollector<'_> {
     fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
         self.body.bindings[binding_id].definitions.push(pat_id);
     }
-}
 
-impl From<ast::LiteralKind> for Literal {
-    fn from(ast_lit_kind: ast::LiteralKind) -> Self {
-        match ast_lit_kind {
-            // FIXME: these should have actual values filled in, but unsure on perf impact
-            LiteralKind::IntNumber(lit) => {
-                if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
-                    Literal::Float(
-                        FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
-                        builtin,
-                    )
-                } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
-                    Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
-                } else {
-                    let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
-                    Literal::Uint(lit.value().unwrap_or(0), builtin)
+    // region: labels
+
+    fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
+        let label = Label {
+            name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
+        };
+        self.alloc_label(label, AstPtr::new(&ast_label))
+    }
+
+    fn resolve_label(
+        &self,
+        lifetime: Option<ast::Lifetime>,
+    ) -> Result<Option<LabelId>, BodyDiagnostic> {
+        let Some(lifetime) = lifetime else {
+            return Ok(None)
+        };
+        let name = Name::new_lifetime(&lifetime);
+
+        for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
+            if let Some((label_name, id)) = &rib.label {
+                if *label_name == name {
+                    return if self.is_label_valid_from_rib(rib_idx) {
+                        Ok(Some(*id))
+                    } else {
+                        Err(BodyDiagnostic::UnreachableLabel {
+                            name,
+                            node: InFile::new(
+                                self.expander.current_file_id,
+                                AstPtr::new(&lifetime),
+                            ),
+                        })
+                    };
                 }
             }
-            LiteralKind::FloatNumber(lit) => {
-                let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
-                Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
-            }
-            LiteralKind::ByteString(bs) => {
-                let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
-                Literal::ByteString(text)
-            }
-            LiteralKind::String(s) => {
-                let text = s.value().map(Box::from).unwrap_or_else(Default::default);
-                Literal::String(text)
-            }
-            LiteralKind::Byte(b) => {
-                Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
-            }
-            LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
-            LiteralKind::Bool(val) => Literal::Bool(val),
         }
+
+        Err(BodyDiagnostic::UndeclaredLabel {
+            name,
+            node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
+        })
     }
+
+    fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+        !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
+    }
+
+    fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
+        self.label_ribs.push(LabelRib::new(kind));
+        let res = f(self);
+        self.label_ribs.pop();
+        res
+    }
+
+    fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
+        self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+        let res = f(self);
+        self.label_ribs.pop();
+        res
+    }
+
+    fn with_opt_labeled_rib<T>(
+        &mut self,
+        label: Option<LabelId>,
+        f: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        match label {
+            None => f(self),
+            Some(label) => self.with_labeled_rib(label, f),
+        }
+    }
+    // endregion: labels
+}
+
+fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
+    let ast_lit = lit.literal()?;
+    let mut hir_lit: Literal = ast_lit.kind().into();
+    if lit.minus_token().is_some() {
+        let Some(h) = hir_lit.negate() else {
+            return None;
+        };
+        hir_lit = h;
+    }
+    Some((hir_lit, ast_lit))
+}
+
+impl ExprCollector<'_> {
+    fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
+        let src = self.expander.to_source(ptr);
+        let id = self.body.exprs.alloc(expr);
+        self.source_map.expr_map_back.insert(id, src.clone());
+        self.source_map.expr_map.insert(src, id);
+        id
+    }
+    // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
+    fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
+        self.body.exprs.alloc(expr)
+    }
+    fn missing_expr(&mut self) -> ExprId {
+        self.alloc_expr_desugared(Expr::Missing)
+    }
+
+    fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+        self.body.bindings.alloc(Binding {
+            name,
+            mode,
+            definitions: SmallVec::new(),
+            owner: self.current_binding_owner,
+            problems: None,
+        })
+    }
+
+    fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
+        let src = self.expander.to_source(ptr);
+        let id = self.body.pats.alloc(pat);
+        self.source_map.pat_map_back.insert(id, src.clone());
+        self.source_map.pat_map.insert(src, id);
+        id
+    }
+    // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
+    fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
+        self.body.pats.alloc(pat)
+    }
+    fn missing_pat(&mut self) -> PatId {
+        self.body.pats.alloc(Pat::Missing)
+    }
+
+    fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
+        let src = self.expander.to_source(ptr);
+        let id = self.body.labels.alloc(label);
+        self.source_map.label_map_back.insert(id, src.clone());
+        self.source_map.label_map.insert(src, id);
+        id
+    }
+    // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
+    fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
+        self.body.labels.alloc(label)
+    }
+}
+
+fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
+    (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
+        .map_or(false, |it| it.kind() == syntax::T![,])
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 5a9b825a253..88380aa355d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -2,10 +2,14 @@
 
 use std::fmt::{self, Write};
 
+use hir_expand::db::ExpandDatabase;
 use syntax::ast::HasName;
 
 use crate::{
-    expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
+    hir::{
+        Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
+        Movability, Statement,
+    },
     pretty::{print_generic_args, print_path, print_type_ref},
     type_ref::TypeRef,
 };
@@ -13,47 +17,70 @@ use crate::{
 use super::*;
 
 pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
-    let needs_semi;
     let header = match owner {
         DefWithBodyId::FunctionId(it) => {
-            needs_semi = false;
             let item_tree_id = it.lookup(db).id;
-            format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+            format!(
+                "fn {}",
+                item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+            )
         }
         DefWithBodyId::StaticId(it) => {
-            needs_semi = true;
             let item_tree_id = it.lookup(db).id;
-            format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+            format!(
+                "static {} = ",
+                item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+            )
         }
         DefWithBodyId::ConstId(it) => {
-            needs_semi = true;
             let item_tree_id = it.lookup(db).id;
             let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
-                Some(name) => name.to_string(),
+                Some(name) => name.display(db.upcast()).to_string(),
                 None => "_".to_string(),
             };
             format!("const {name} = ")
         }
         DefWithBodyId::VariantId(it) => {
-            needs_semi = false;
             let src = it.parent.child_source(db);
             let variant = &src.value[it.local_id];
-            let name = match &variant.name() {
+            match &variant.name() {
                 Some(name) => name.to_string(),
                 None => "_".to_string(),
-            };
-            format!("{name}")
+            }
         }
     };
 
-    let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
+    let mut p =
+        Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+    if let DefWithBodyId::FunctionId(it) = owner {
+        p.buf.push('(');
+        body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
+            p.print_pat(param);
+            p.buf.push(':');
+            p.print_type_ref(ty);
+        });
+        p.buf.push(')');
+        p.buf.push(' ');
+    }
     p.print_expr(body.body_expr);
-    if needs_semi {
+    if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
         p.buf.push(';');
     }
     p.buf
 }
 
+pub(super) fn print_expr_hir(
+    db: &dyn DefDatabase,
+    body: &Body,
+    _owner: DefWithBodyId,
+    expr: ExprId,
+) -> String {
+    let mut p =
+        Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+    p.print_expr(expr);
+    p.buf
+}
+
 macro_rules! w {
     ($dst:expr, $($arg:tt)*) => {
         { let _ = write!($dst, $($arg)*); }
@@ -70,6 +97,7 @@ macro_rules! wln {
 }
 
 struct Printer<'a> {
+    db: &'a dyn ExpandDatabase,
     body: &'a Body,
     buf: String,
     indent_level: usize,
@@ -144,29 +172,19 @@ impl<'a> Printer<'a> {
             }
             Expr::Loop { body, label } => {
                 if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name);
+                    w!(self, "{}: ", self.body[*lbl].name.display(self.db));
                 }
                 w!(self, "loop ");
                 self.print_expr(*body);
             }
             Expr::While { condition, body, label } => {
                 if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name);
+                    w!(self, "{}: ", self.body[*lbl].name.display(self.db));
                 }
                 w!(self, "while ");
                 self.print_expr(*condition);
                 self.print_expr(*body);
             }
-            Expr::For { iterable, pat, body, label } => {
-                if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name);
-                }
-                w!(self, "for ");
-                self.print_pat(*pat);
-                w!(self, " in ");
-                self.print_expr(*iterable);
-                self.print_expr(*body);
-            }
             Expr::Call { callee, args, is_assignee_expr: _ } => {
                 self.print_expr(*callee);
                 w!(self, "(");
@@ -182,10 +200,10 @@ impl<'a> Printer<'a> {
             }
             Expr::MethodCall { receiver, method_name, args, generic_args } => {
                 self.print_expr(*receiver);
-                w!(self, ".{}", method_name);
+                w!(self, ".{}", method_name.display(self.db));
                 if let Some(args) = generic_args {
                     w!(self, "::<");
-                    print_generic_args(args, self).unwrap();
+                    print_generic_args(self.db, args, self).unwrap();
                     w!(self, ">");
                 }
                 w!(self, "(");
@@ -219,14 +237,14 @@ impl<'a> Printer<'a> {
             }
             Expr::Continue { label } => {
                 w!(self, "continue");
-                if let Some(label) = label {
-                    w!(self, " {}", label);
+                if let Some(lbl) = label {
+                    w!(self, " {}", self.body[*lbl].name.display(self.db));
                 }
             }
             Expr::Break { expr, label } => {
                 w!(self, "break");
-                if let Some(label) = label {
-                    w!(self, " {}", label);
+                if let Some(lbl) = label {
+                    w!(self, " {}", self.body[*lbl].name.display(self.db));
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
@@ -265,7 +283,7 @@ impl<'a> Printer<'a> {
                 w!(self, "{{");
                 self.indented(|p| {
                     for field in &**fields {
-                        w!(p, "{}: ", field.name);
+                        w!(p, "{}: ", field.name.display(self.db));
                         p.print_expr(field.expr);
                         wln!(p, ",");
                     }
@@ -282,16 +300,12 @@ impl<'a> Printer<'a> {
             }
             Expr::Field { expr, name } => {
                 self.print_expr(*expr);
-                w!(self, ".{}", name);
+                w!(self, ".{}", name.display(self.db));
             }
             Expr::Await { expr } => {
                 self.print_expr(*expr);
                 w!(self, ".await");
             }
-            Expr::Try { expr } => {
-                self.print_expr(*expr);
-                w!(self, "?");
-            }
             Expr::Cast { expr, type_ref } => {
                 self.print_expr(*expr);
                 w!(self, " as ");
@@ -359,7 +373,7 @@ impl<'a> Printer<'a> {
                 self.print_expr(*index);
                 w!(self, "]");
             }
-            Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
+            Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
                 match closure_kind {
                     ClosureKind::Generator(Movability::Static) => {
                         w!(self, "static ");
@@ -369,6 +383,12 @@ impl<'a> Printer<'a> {
                     }
                     _ => (),
                 }
+                match capture_by {
+                    CaptureBy::Value => {
+                        w!(self, "move ");
+                    }
+                    CaptureBy::Ref => (),
+                }
                 w!(self, "|");
                 for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
                     if i != 0 {
@@ -418,20 +438,17 @@ impl<'a> Printer<'a> {
             }
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
-                let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+                let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
                 self.print_block(label.as_deref(), statements, tail);
             }
             Expr::Unsafe { id: _, statements, tail } => {
                 self.print_block(Some("unsafe "), statements, tail);
             }
-            Expr::TryBlock { id: _, statements, tail } => {
-                self.print_block(Some("try "), statements, tail);
-            }
             Expr::Async { id: _, statements, tail } => {
                 self.print_block(Some("async "), statements, tail);
             }
-            Expr::Const { id: _, statements, tail } => {
-                self.print_block(Some("const "), statements, tail);
+            Expr::Const(id) => {
+                w!(self, "const {{ /* {id:?} */ }}");
             }
         }
     }
@@ -439,7 +456,7 @@ impl<'a> Printer<'a> {
     fn print_block(
         &mut self,
         label: Option<&str>,
-        statements: &Box<[Statement]>,
+        statements: &[Statement],
         tail: &Option<la_arena::Idx<Expr>>,
     ) {
         self.whitespace();
@@ -449,7 +466,7 @@ impl<'a> Printer<'a> {
         w!(self, "{{");
         if !statements.is_empty() || tail.is_some() {
             self.indented(|p| {
-                for stmt in &**statements {
+                for stmt in statements {
                     p.print_stmt(stmt);
                 }
                 if let Some(tail) = tail {
@@ -497,7 +514,7 @@ impl<'a> Printer<'a> {
                 w!(self, " {{");
                 self.indented(|p| {
                     for arg in args.iter() {
-                        w!(p, "{}: ", arg.name);
+                        w!(p, "{}: ", arg.name.display(self.db));
                         p.print_pat(arg.pat);
                         wln!(p, ",");
                     }
@@ -508,9 +525,13 @@ impl<'a> Printer<'a> {
                 w!(self, "}}");
             }
             Pat::Range { start, end } => {
-                self.print_expr(*start);
-                w!(self, "...");
-                self.print_expr(*end);
+                if let Some(start) = start {
+                    self.print_literal_or_const(start);
+                }
+                w!(self, "..=");
+                if let Some(end) = end {
+                    self.print_literal_or_const(end);
+                }
             }
             Pat::Slice { prefix, slice, suffix } => {
                 w!(self, "[");
@@ -601,10 +622,18 @@ impl<'a> Printer<'a> {
         }
     }
 
+    fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+        match literal_or_const {
+            LiteralOrConst::Literal(l) => self.print_literal(l),
+            LiteralOrConst::Const(c) => self.print_path(c),
+        }
+    }
+
     fn print_literal(&mut self, literal: &Literal) {
         match literal {
             Literal::String(it) => w!(self, "{:?}", it),
             Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
+            Literal::CString(it) => w!(self, "\"{}\\0\"", it),
             Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
             Literal::Bool(it) => w!(self, "{}", it),
             Literal::Int(i, suffix) => {
@@ -629,11 +658,11 @@ impl<'a> Printer<'a> {
     }
 
     fn print_type_ref(&mut self, ty: &TypeRef) {
-        print_type_ref(ty, self).unwrap();
+        print_type_ref(self.db, ty, self).unwrap();
     }
 
     fn print_path(&mut self, path: &Path) {
-        print_path(path, self).unwrap();
+        print_path(self.db, path, self).unwrap();
     }
 
     fn print_binding(&mut self, id: BindingId) {
@@ -644,6 +673,6 @@ impl<'a> Printer<'a> {
             BindingAnnotation::Ref => "ref ",
             BindingAnnotation::RefMut => "ref mut ",
         };
-        w!(self, "{}{}", mode, name);
+        w!(self, "{}{}", mode, name.display(self.db));
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
index 12fc1f116d7..69741c445fb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
@@ -1,14 +1,13 @@
 //! Name resolution for expressions.
-use std::sync::Arc;
-
 use hir_expand::name::Name;
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, IdxRange, RawIdx};
 use rustc_hash::FxHashMap;
+use triomphe::Arc;
 
 use crate::{
     body::Body,
     db::DefDatabase,
-    expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
+    hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
     BlockId, DefWithBodyId,
 };
 
@@ -17,6 +16,7 @@ pub type ScopeId = Idx<ScopeData>;
 #[derive(Debug, PartialEq, Eq)]
 pub struct ExprScopes {
     scopes: Arena<ScopeData>,
+    scope_entries: Arena<ScopeEntry>,
     scope_by_expr: FxHashMap<ExprId, ScopeId>,
 }
 
@@ -41,7 +41,7 @@ pub struct ScopeData {
     parent: Option<ScopeId>,
     block: Option<BlockId>,
     label: Option<(LabelId, Name)>,
-    entries: Vec<ScopeEntry>,
+    entries: IdxRange<ScopeEntry>,
 }
 
 impl ExprScopes {
@@ -53,7 +53,7 @@ impl ExprScopes {
     }
 
     pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
-        &self.scopes[scope].entries
+        &self.scope_entries[self.scopes[scope].entries.clone()]
     }
 
     /// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
@@ -85,10 +85,17 @@ impl ExprScopes {
     }
 }
 
+fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
+    IdxRange::new(Idx::from_raw(RawIdx::from(idx as u32))..Idx::from_raw(RawIdx::from(idx as u32)))
+}
+
 impl ExprScopes {
     fn new(body: &Body) -> ExprScopes {
-        let mut scopes =
-            ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
+        let mut scopes = ExprScopes {
+            scopes: Arena::default(),
+            scope_entries: Arena::default(),
+            scope_by_expr: FxHashMap::default(),
+        };
         let mut root = scopes.root_scope();
         scopes.add_params_bindings(body, root, &body.params);
         compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
@@ -96,7 +103,12 @@ impl ExprScopes {
     }
 
     fn root_scope(&mut self) -> ScopeId {
-        self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] })
+        self.scopes.alloc(ScopeData {
+            parent: None,
+            block: None,
+            label: None,
+            entries: empty_entries(self.scope_entries.len()),
+        })
     }
 
     fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
@@ -104,32 +116,38 @@ impl ExprScopes {
             parent: Some(parent),
             block: None,
             label: None,
-            entries: vec![],
+            entries: empty_entries(self.scope_entries.len()),
         })
     }
 
     fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId {
-        self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] })
+        self.scopes.alloc(ScopeData {
+            parent: Some(parent),
+            block: None,
+            label,
+            entries: empty_entries(self.scope_entries.len()),
+        })
     }
 
     fn new_block_scope(
         &mut self,
         parent: ScopeId,
-        block: BlockId,
+        block: Option<BlockId>,
         label: Option<(LabelId, Name)>,
     ) -> ScopeId {
         self.scopes.alloc(ScopeData {
             parent: Some(parent),
-            block: Some(block),
+            block,
             label,
-            entries: vec![],
+            entries: empty_entries(self.scope_entries.len()),
         })
     }
 
     fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
         let Binding { name, .. } = &body.bindings[binding];
-        let entry = ScopeEntry { name: name.clone(), binding };
-        self.scopes[scope].entries.push(entry);
+        let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
+        self.scopes[scope].entries =
+            IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
     }
 
     fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
@@ -150,9 +168,9 @@ impl ExprScopes {
     }
 
     fn shrink_to_fit(&mut self) {
-        let ExprScopes { scopes, scope_by_expr } = self;
+        let ExprScopes { scopes, scope_entries, scope_by_expr } = self;
         scopes.shrink_to_fit();
-        scopes.values_mut().for_each(|it| it.entries.shrink_to_fit());
+        scope_entries.shrink_to_fit();
         scope_by_expr.shrink_to_fit();
     }
 }
@@ -200,22 +218,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             scopes.set_scope(expr, scope);
             compute_block_scopes(statements, *tail, body, scopes, &mut scope);
         }
-        Expr::Unsafe { id, statements, tail }
-        | Expr::Async { id, statements, tail }
-        | Expr::Const { id, statements, tail }
-        | Expr::TryBlock { id, statements, tail } => {
+        Expr::Const(_) => {
+            // FIXME: This is broken.
+        }
+        Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
             let mut scope = scopes.new_block_scope(*scope, *id, None);
             // Overwrite the old scope for the block expr, so that every block scope can be found
             // via the block itself (important for blocks that only contain items, no expressions).
             scopes.set_scope(expr, scope);
             compute_block_scopes(statements, *tail, body, scopes, &mut scope);
         }
-        Expr::For { iterable, pat, body: body_expr, label } => {
-            compute_expr_scopes(*iterable, body, scopes, scope);
-            let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
-            scopes.add_pat_bindings(body, scope, *pat);
-            compute_expr_scopes(*body_expr, body, scopes, &mut scope);
-        }
         Expr::While { condition, body: body_expr, label } => {
             let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
             compute_expr_scopes(*condition, body, scopes, &mut scope);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
index 77ac221e590..6e77744f215 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
@@ -148,8 +148,8 @@ fn f() {
 }
     "#,
         expect![[r#"
-            BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
-            BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
+            BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
+            BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
             crate scope
         "#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
index dd69c3ab473..61b2481978e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
@@ -106,8 +106,14 @@ impl AsName for BuiltinType {
 
 impl fmt::Display for BuiltinType {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let type_name = self.as_name();
-        type_name.fmt(f)
+        match self {
+            BuiltinType::Char => f.write_str("char"),
+            BuiltinType::Bool => f.write_str("bool"),
+            BuiltinType::Str => f.write_str("str"),
+            BuiltinType::Int(it) => it.fmt(f),
+            BuiltinType::Uint(it) => it.fmt(f),
+            BuiltinType::Float(it) => it.fmt(f),
+        }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 68b57acca2a..bb79e28f267 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -10,9 +10,9 @@ use syntax::ast::HasDocComments;
 
 use crate::{
     db::DefDatabase,
-    dyn_map::DynMap,
+    dyn_map::{keys, DynMap},
     item_scope::ItemScope,
-    keys,
+    nameres::DefMap,
     src::{HasChildSource, HasSource},
     AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
     ModuleDefId, ModuleId, TraitId, VariantId,
@@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId {
         for (_, def_map) in body.blocks(db) {
             // All block expressions are merged into the same map, because they logically all add
             // inner items to the containing `DefWithBodyId`.
-            def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
+            def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 1633a33bedd..40e6a430878 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -1,22 +1,28 @@
 //! Contains basic data about various HIR declarations.
 
-use std::sync::Arc;
+pub mod adt;
 
-use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
+use hir_expand::{
+    name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
+};
 use intern::Interned;
 use smallvec::SmallVec;
-use syntax::ast;
+use syntax::{ast, Parse};
+use triomphe::Arc;
 
 use crate::{
     attr::Attrs,
-    body::{Expander, Mark},
     db::DefDatabase,
-    item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
+    expander::{Expander, Mark},
+    item_tree::{
+        self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId,
+    },
+    macro_call_as_call_id, macro_id_to_def_id,
     nameres::{
         attr_resolution::ResolvedAttr,
         diagnostics::DefDiagnostic,
         proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
-        DefMap,
+        DefMap, MacroSubNs,
     },
     type_ref::{TraitRef, TypeBound, TypeRef},
     visibility::RawVisibility,
@@ -28,9 +34,8 @@ use crate::{
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct FunctionData {
     pub name: Name,
-    pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
+    pub params: Vec<Interned<TypeRef>>,
     pub ret_type: Interned<TypeRef>,
-    pub async_ret_type: Option<Interned<TypeRef>>,
     pub attrs: Attrs,
     pub visibility: RawVisibility,
     pub abi: Option<Interned<str>>,
@@ -43,16 +48,16 @@ impl FunctionData {
     pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
         let loc = func.lookup(db);
         let krate = loc.container.module(db).krate;
-        let crate_graph = db.crate_graph();
-        let cfg_options = &crate_graph[krate].cfg_options;
         let item_tree = loc.id.item_tree(db);
         let func = &item_tree[loc.id.value];
         let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
-            db.trait_data(trait_id).visibility.clone()
+            trait_vis(db, trait_id)
         } else {
             item_tree[func.visibility].clone()
         };
 
+        let crate_graph = db.crate_graph();
+        let cfg_options = &crate_graph[krate].cfg_options;
         let enabled_params = func
             .params
             .clone()
@@ -99,12 +104,11 @@ impl FunctionData {
             params: enabled_params
                 .clone()
                 .filter_map(|id| match &item_tree[id] {
-                    Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
+                    Param::Normal(ty) => Some(ty.clone()),
                     Param::Varargs => None,
                 })
                 .collect(),
             ret_type: func.ret_type.clone(),
-            async_ret_type: func.async_ret_type.clone(),
             attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
             visibility,
             abi: func.abi.clone(),
@@ -188,7 +192,7 @@ impl TypeAliasData {
         let item_tree = loc.id.item_tree(db);
         let typ = &item_tree[loc.id.value];
         let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
-            db.trait_data(trait_id).visibility.clone()
+            trait_vis(db, trait_id)
         } else {
             item_tree[typ.visibility].clone()
         };
@@ -471,7 +475,7 @@ impl ConstData {
         let item_tree = loc.id.item_tree(db);
         let konst = &item_tree[loc.id.value];
         let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
-            db.trait_data(trait_id).visibility.clone()
+            trait_vis(db, trait_id)
         } else {
             item_tree[konst.visibility].clone()
         };
@@ -519,7 +523,7 @@ struct AssocItemCollector<'a> {
     db: &'a dyn DefDatabase,
     module_id: ModuleId,
     def_map: Arc<DefMap>,
-    inactive_diagnostics: Vec<DefDiagnostic>,
+    diagnostics: Vec<DefDiagnostic>,
     container: ItemContainerId,
     expander: Expander,
 
@@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> {
             expander: Expander::new(db, file_id, module_id),
             items: Vec::new(),
             attr_calls: Vec::new(),
-            inactive_diagnostics: Vec::new(),
+            diagnostics: Vec::new(),
         }
     }
 
@@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> {
         (
             self.items,
             if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
-            self.inactive_diagnostics,
+            self.diagnostics,
         )
     }
 
-    // FIXME: proc-macro diagnostics
     fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
         let container = self.container;
         self.items.reserve(assoc_items.len());
@@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> {
         'items: for &item in assoc_items {
             let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
             if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
-                self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
+                self.diagnostics.push(DefDiagnostic::unconfigured_code(
                     self.module_id.local_id,
                     InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
                     attrs.cfg().unwrap(),
@@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> {
                     AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
                 let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
 
-                if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
+                match self.def_map.resolve_attr_macro(
                     self.db,
                     self.module_id.local_id,
                     ast_id_with_path,
                     attr,
                 ) {
-                    self.attr_calls.push((ast_id, call_id));
-                    // If proc attribute macro expansion is disabled, skip expanding it here
-                    if !self.db.enable_proc_attr_macros() {
-                        continue 'attrs;
-                    }
-                    let loc = self.db.lookup_intern_macro_call(call_id);
-                    if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
-                        // If there's no expander for the proc macro (e.g. the
-                        // proc macro is ignored, or building the proc macro
-                        // crate failed), skip expansion like we would if it was
-                        // disabled. This is analogous to the handling in
-                        // `DefCollector::collect_macros`.
-                        if exp.is_dummy() {
+                    Ok(ResolvedAttr::Macro(call_id)) => {
+                        self.attr_calls.push((ast_id, call_id));
+                        // If proc attribute macro expansion is disabled, skip expanding it here
+                        if !self.db.expand_proc_attr_macros() {
                             continue 'attrs;
                         }
-                    }
-                    match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
-                        ExpandResult { value: Some((mark, _)), .. } => {
-                            self.collect_macro_items(mark);
-                            continue 'items;
+                        let loc = self.db.lookup_intern_macro_call(call_id);
+                        if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
+                            // If there's no expander for the proc macro (e.g. the
+                            // proc macro is ignored, or building the proc macro
+                            // crate failed), skip expansion like we would if it was
+                            // disabled. This is analogous to the handling in
+                            // `DefCollector::collect_macros`.
+                            if exp.is_dummy() {
+                                continue 'attrs;
+                            }
                         }
-                        ExpandResult { .. } => {}
+
+                        let res =
+                            self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+                        self.collect_macro_items(res, &|| loc.kind.clone());
+                        continue 'items;
+                    }
+                    Ok(_) => (),
+                    Err(_) => {
+                        self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+                            self.module_id.local_id,
+                            MacroCallKind::Attr {
+                                ast_id,
+                                attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
+                                invoc_attr_index: attr.id,
+                            },
+                            attr.path().clone(),
+                        ));
                     }
                 }
             }
 
-            match item {
-                AssocItem::Function(id) => {
-                    let item = &item_tree[id];
+            self.collect_item(item_tree, tree_id, container, item);
+        }
+    }
 
-                    let def =
-                        FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
-                    self.items.push((item.name.clone(), def.into()));
-                }
-                AssocItem::Const(id) => {
-                    let item = &item_tree[id];
-
-                    let name = match item.name.clone() {
-                        Some(name) => name,
-                        None => continue,
-                    };
-                    let def =
-                        ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
-                    self.items.push((name, def.into()));
-                }
-                AssocItem::TypeAlias(id) => {
-                    let item = &item_tree[id];
+    fn collect_item(
+        &mut self,
+        item_tree: &ItemTree,
+        tree_id: TreeId,
+        container: ItemContainerId,
+        item: AssocItem,
+    ) {
+        match item {
+            AssocItem::Function(id) => {
+                let item = &item_tree[id];
 
-                    let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }
-                        .intern(self.db);
-                    self.items.push((item.name.clone(), def.into()));
-                }
-                AssocItem::MacroCall(call) => {
-                    if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) {
-                        let call = &item_tree[call];
-
-                        let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
-                        let call = ast_id_map.get(call.ast_id).to_node(&root);
-                        let _cx =
-                            stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
-                        let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
-
-                        if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {
-                            self.collect_macro_items(mark);
-                        }
+                let def =
+                    FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+                self.items.push((item.name.clone(), def.into()));
+            }
+            AssocItem::Const(id) => {
+                let item = &item_tree[id];
+                let Some(name) = item.name.clone() else { return };
+                let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+                self.items.push((name, def.into()));
+            }
+            AssocItem::TypeAlias(id) => {
+                let item = &item_tree[id];
+
+                let def =
+                    TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+                self.items.push((item.name.clone(), def.into()));
+            }
+            AssocItem::MacroCall(call) => {
+                let file_id = self.expander.current_file_id();
+                let MacroCall { ast_id, expand_to, ref path } = item_tree[call];
+                let module = self.expander.module.local_id;
+
+                let resolver = |path| {
+                    self.def_map
+                        .resolve_path(
+                            self.db,
+                            module,
+                            &path,
+                            crate::item_scope::BuiltinShadowMode::Other,
+                            Some(MacroSubNs::Bang),
+                        )
+                        .0
+                        .take_macros()
+                        .map(|it| macro_id_to_def_id(self.db, it))
+                };
+                match macro_call_as_call_id(
+                    self.db.upcast(),
+                    &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
+                    expand_to,
+                    self.expander.module.krate(),
+                    resolver,
+                ) {
+                    Ok(Some(call_id)) => {
+                        let res =
+                            self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+                        self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
+                            ast_id: InFile::new(file_id, ast_id),
+                            expand_to: hir_expand::ExpandTo::Items,
+                        });
+                    }
+                    Ok(None) => (),
+                    Err(_) => {
+                        self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+                            self.module_id.local_id,
+                            MacroCallKind::FnLike {
+                                ast_id: InFile::new(file_id, ast_id),
+                                expand_to,
+                            },
+                            Clone::clone(path),
+                        ));
                     }
                 }
             }
         }
     }
 
-    fn collect_macro_items(&mut self, mark: Mark) {
+    fn collect_macro_items(
+        &mut self,
+        ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
+        error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
+    ) {
+        let Some((mark, parse)) = value else { return };
+
+        if let Some(err) = err {
+            let diag = match err {
+                // why is this reported here?
+                hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
+                    DefDiagnostic::unresolved_proc_macro(
+                        self.module_id.local_id,
+                        error_call_kind(),
+                        krate,
+                    )
+                }
+                _ => DefDiagnostic::macro_error(
+                    self.module_id.local_id,
+                    error_call_kind(),
+                    err.to_string(),
+                ),
+            };
+            self.diagnostics.push(diag);
+        }
+        if let errors @ [_, ..] = parse.errors() {
+            self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
+                self.module_id.local_id,
+                error_call_kind(),
+                errors.into(),
+            ));
+        }
+
         let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
         let item_tree = tree_id.item_tree(self.db);
         let iter: SmallVec<[_; 2]> =
@@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> {
         self.expander.exit(self.db, mark);
     }
 }
+
+fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility {
+    let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
+    let item_tree = tree_id.item_tree(db);
+    let tr_def = &item_tree[tree_id.value];
+    item_tree[tr_def.visibility].clone()
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index b336f59ffee..6db5abccc92 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -1,8 +1,7 @@
 //! Defines hir-level representation of structs, enums and unions
 
-use std::sync::Arc;
-
 use base_db::CrateId;
+use bitflags::bitflags;
 use cfg::CfgOptions;
 use either::Either;
 
@@ -12,15 +11,17 @@ use hir_expand::{
 };
 use intern::Interned;
 use la_arena::{Arena, ArenaMap};
-use rustc_abi::{Integer, IntegerType};
+use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
 use syntax::ast::{self, HasName, HasVisibility};
+use triomphe::Arc;
 
 use crate::{
-    body::{CfgExpander, LowerCtx},
     builtin_type::{BuiltinInt, BuiltinUint},
     db::DefDatabase,
+    expander::CfgExpander,
     item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
-    layout::{Align, ReprFlags, ReprOptions},
+    lang_item::LangItem,
+    lower::LowerCtx,
     nameres::diagnostics::DefDiagnostic,
     src::HasChildSource,
     src::HasSource,
@@ -39,8 +40,27 @@ pub struct StructData {
     pub variant_data: Arc<VariantData>,
     pub repr: Option<ReprOptions>,
     pub visibility: RawVisibility,
-    pub rustc_has_incoherent_inherent_impls: bool,
-    pub fundamental: bool,
+    pub flags: StructFlags,
+}
+
+bitflags! {
+#[derive(Debug, Clone, PartialEq, Eq)]
+    pub struct StructFlags: u8 {
+        const NO_FLAGS         = 0;
+        /// Indicates whether the struct is `PhantomData`.
+        const IS_PHANTOM_DATA  = 1 << 2;
+        /// Indicates whether the struct has a `#[fundamental]` attribute.
+        const IS_FUNDAMENTAL   = 1 << 3;
+        // FIXME: should this be a flag?
+        /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
+        const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL      = 1 << 4;
+        /// Indicates whether this struct is `Box`.
+        const IS_BOX           = 1 << 5;
+        /// Indicates whether this struct is `ManuallyDrop`.
+        const IS_MANUALLY_DROP = 1 << 6;
+        /// Indicates whether this struct is `UnsafeCell`.
+        const IS_UNSAFE_CELL   = 1 << 7;
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -174,10 +194,25 @@ impl StructData {
         let item_tree = loc.id.item_tree(db);
         let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+
         let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
-        let rustc_has_incoherent_inherent_impls =
-            attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
-        let fundamental = attrs.by_key("fundamental").exists();
+
+        let mut flags = StructFlags::NO_FLAGS;
+        if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+            flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+        }
+        if attrs.by_key("fundamental").exists() {
+            flags |= StructFlags::IS_FUNDAMENTAL;
+        }
+        if let Some(lang) = attrs.lang_item() {
+            match lang {
+                LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
+                LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
+                LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
+                LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
+                _ => (),
+            }
+        }
 
         let strukt = &item_tree[loc.id.value];
         let (variant_data, diagnostics) = lower_fields(
@@ -196,8 +231,7 @@ impl StructData {
                 variant_data: Arc::new(variant_data),
                 repr,
                 visibility: item_tree[strukt.visibility].clone(),
-                rustc_has_incoherent_inherent_impls,
-                fundamental,
+                flags,
             }),
             diagnostics.into(),
         )
@@ -218,9 +252,13 @@ impl StructData {
         let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
 
         let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
-        let rustc_has_incoherent_inherent_impls =
-            attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
-        let fundamental = attrs.by_key("fundamental").exists();
+        let mut flags = StructFlags::NO_FLAGS;
+        if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+            flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+        }
+        if attrs.by_key("fundamental").exists() {
+            flags |= StructFlags::IS_FUNDAMENTAL;
+        }
 
         let union = &item_tree[loc.id.value];
         let (variant_data, diagnostics) = lower_fields(
@@ -239,8 +277,7 @@ impl StructData {
                 variant_data: Arc::new(variant_data),
                 repr,
                 visibility: item_tree[union.visibility].clone(),
-                rustc_has_incoherent_inherent_impls,
-                fundamental,
+                flags,
             }),
             diagnostics.into(),
         )
@@ -436,7 +473,7 @@ fn lower_struct(
     trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
     ast: &InFile<ast::StructKind>,
 ) -> StructKind {
-    let ctx = LowerCtx::new(db, ast.file_id);
+    let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
 
     match &ast.value {
         ast::StructKind::Tuple(fl) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 9371fc14dd8..6d18e3f56ca 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,32 +1,32 @@
 //! Defines database & queries for name resolution.
-use std::sync::Arc;
-
 use base_db::{salsa, CrateId, SourceDatabase, Upcast};
 use either::Either;
 use hir_expand::{db::ExpandDatabase, HirFileId};
 use intern::Interned;
 use la_arena::ArenaMap;
 use syntax::{ast, AstPtr};
+use triomphe::Arc;
 
 use crate::{
-    adt::{EnumData, StructData},
     attr::{Attrs, AttrsWithOwner},
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
+        adt::{EnumData, StructData},
         ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
         TraitAliasData, TraitData, TypeAliasData,
     },
     generics::GenericParams,
+    hir::ExprId,
     import_map::ImportMap,
     item_tree::{AttrOwner, ItemTree},
     lang_item::{LangItem, LangItemTarget, LangItems},
     nameres::{diagnostics::DefDiagnostic, DefMap},
     visibility::{self, Visibility},
-    AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
-    ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
-    LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
-    StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
-    TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
+    AnonymousConstId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId,
+    EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc,
+    LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc,
+    ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId,
+    TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
 };
 
 #[salsa::query_group(InternDatabaseStorage)]
@@ -61,12 +61,14 @@ pub trait InternDatabase: SourceDatabase {
     fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
     #[salsa::interned]
     fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
+    #[salsa::interned]
+    fn intern_anonymous_const(&self, id: (DefWithBodyId, ExprId)) -> AnonymousConstId;
 }
 
 #[salsa::query_group(DefDatabaseStorage)]
 pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
     #[salsa::input]
-    fn enable_proc_attr_macros(&self) -> bool;
+    fn expand_proc_attr_macros(&self) -> bool;
 
     #[salsa::invoke(ItemTree::file_item_tree_query)]
     fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
@@ -94,7 +96,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     /// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would
     /// return a `DefMap` containing `inner`.
     #[salsa::invoke(DefMap::block_def_map_query)]
-    fn block_def_map(&self, block: BlockId) -> Option<Arc<DefMap>>;
+    fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
+
+    // region:data
 
     #[salsa::invoke(StructData::struct_data_query)]
     fn struct_data(&self, id: StructId) -> Arc<StructData>;
@@ -151,6 +155,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     #[salsa::invoke(ProcMacroData::proc_macro_data_query)]
     fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
 
+    // endregion:data
+
     #[salsa::invoke(Body::body_with_source_map_query)]
     fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
 
@@ -163,6 +169,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     #[salsa::invoke(GenericParams::generic_params_query)]
     fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
 
+    // region:attrs
+
     #[salsa::invoke(Attrs::variants_attrs_query)]
     fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
 
@@ -182,10 +190,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
 
     #[salsa::invoke(AttrsWithOwner::attrs_query)]
-    fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
+    fn attrs(&self, def: AttrDefId) -> Attrs;
 
-    #[salsa::invoke(LangItems::crate_lang_items_query)]
-    fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+    #[salsa::transparent]
+    #[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
+    fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
+
+    // endregion:attrs
 
     #[salsa::invoke(LangItems::lang_item_query)]
     fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
@@ -193,6 +204,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     #[salsa::invoke(ImportMap::import_map_query)]
     fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
 
+    // region:visibilities
+
     #[salsa::invoke(visibility::field_visibilities_query)]
     fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
 
@@ -203,9 +216,17 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     #[salsa::invoke(visibility::const_visibility_query)]
     fn const_visibility(&self, def: ConstId) -> Visibility;
 
+    // endregion:visibilities
+
+    #[salsa::invoke(LangItems::crate_lang_items_query)]
+    fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+
     #[salsa::transparent]
     fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
 
+    #[salsa::transparent]
+    fn recursion_limit(&self, crate_id: CrateId) -> u32;
+
     fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
 }
 
@@ -228,6 +249,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
     }
 }
 
+fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 {
+    db.crate_limits(crate_id).recursion_limit
+}
+
 fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
     let file = db.crate_graph()[crate_id].root_file_id;
     let item_tree = db.file_item_tree(file.into());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
index 166aa04da04..63138aa6ad7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
@@ -21,6 +21,8 @@
 //!
 //! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
 //! a coincidence.
+pub mod keys;
+
 use std::{
     hash::Hash,
     marker::PhantomData,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
index f30be6b64e3..f30be6b64e3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
new file mode 100644
index 00000000000..34ed1e72f20
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -0,0 +1,211 @@
+//! Macro expansion utilities.
+
+use base_db::CrateId;
+use cfg::CfgOptions;
+use drop_bomb::DropBomb;
+use hir_expand::{
+    attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId,
+    InFile, MacroCallId, UnresolvedMacro,
+};
+use limit::Limit;
+use syntax::{ast, Parse, SyntaxNode};
+
+use crate::{
+    attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
+    MacroId, ModuleId,
+};
+
+/// A subset of Expander that only deals with cfg attributes. We only need it to
+/// avoid cyclic queries in crate def map during enum processing.
+#[derive(Debug)]
+pub(crate) struct CfgExpander {
+    cfg_options: CfgOptions,
+    hygiene: Hygiene,
+    krate: CrateId,
+}
+
+#[derive(Debug)]
+pub struct Expander {
+    cfg_expander: CfgExpander,
+    pub(crate) current_file_id: HirFileId,
+    pub(crate) module: ModuleId,
+    /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
+    recursion_depth: u32,
+    recursion_limit: Limit,
+}
+
+impl CfgExpander {
+    pub(crate) fn new(
+        db: &dyn DefDatabase,
+        current_file_id: HirFileId,
+        krate: CrateId,
+    ) -> CfgExpander {
+        let hygiene = Hygiene::new(db.upcast(), current_file_id);
+        let cfg_options = db.crate_graph()[krate].cfg_options.clone();
+        CfgExpander { cfg_options, hygiene, krate }
+    }
+
+    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+        Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
+    }
+
+    pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
+        let attrs = self.parse_attrs(db, owner);
+        attrs.is_cfg_enabled(&self.cfg_options)
+    }
+
+    pub(crate) fn hygiene(&self) -> &Hygiene {
+        &self.hygiene
+    }
+}
+
+impl Expander {
+    pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
+        let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
+        let recursion_limit = db.recursion_limit(module.krate);
+        #[cfg(not(test))]
+        let recursion_limit = Limit::new(recursion_limit as usize);
+        // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
+        #[cfg(test)]
+        let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize));
+        Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit }
+    }
+
+    pub fn enter_expand<T: ast::AstNode>(
+        &mut self,
+        db: &dyn DefDatabase,
+        macro_call: ast::MacroCall,
+        resolver: impl Fn(ModPath) -> Option<MacroId>,
+    ) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
+        // FIXME: within_limit should support this, instead of us having to extract the error
+        let mut unresolved_macro_err = None;
+
+        let result = self.within_limit(db, |this| {
+            let macro_call = InFile::new(this.current_file_id, &macro_call);
+            match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| {
+                resolver(path).map(|it| macro_id_to_def_id(db, it))
+            }) {
+                Ok(call_id) => call_id,
+                Err(resolve_err) => {
+                    unresolved_macro_err = Some(resolve_err);
+                    ExpandResult { value: None, err: None }
+                }
+            }
+        });
+
+        if let Some(err) = unresolved_macro_err {
+            Err(err)
+        } else {
+            Ok(result)
+        }
+    }
+
+    pub fn enter_expand_id<T: ast::AstNode>(
+        &mut self,
+        db: &dyn DefDatabase,
+        call_id: MacroCallId,
+    ) -> ExpandResult<Option<(Mark, Parse<T>)>> {
+        self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
+    }
+
+    fn enter_expand_inner(
+        db: &dyn DefDatabase,
+        call_id: MacroCallId,
+        error: Option<ExpandError>,
+    ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
+        let file_id = call_id.as_file();
+        let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id);
+
+        ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) }
+    }
+
+    pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
+        self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
+        self.current_file_id = mark.file_id;
+        if self.recursion_depth == u32::MAX {
+            // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
+            // depth only when we get out of the tree.
+            if !self.current_file_id.is_macro() {
+                self.recursion_depth = 0;
+            }
+        } else {
+            self.recursion_depth -= 1;
+        }
+        mark.bomb.defuse();
+    }
+
+    pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
+        LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
+    }
+
+    pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
+        InFile { file_id: self.current_file_id, value }
+    }
+
+    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+        self.cfg_expander.parse_attrs(db, owner)
+    }
+
+    pub(crate) fn cfg_options(&self) -> &CfgOptions {
+        &self.cfg_expander.cfg_options
+    }
+
+    pub fn current_file_id(&self) -> HirFileId {
+        self.current_file_id
+    }
+
+    pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
+        let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
+        Path::from_src(path, &ctx)
+    }
+
+    fn within_limit<F, T: ast::AstNode>(
+        &mut self,
+        db: &dyn DefDatabase,
+        op: F,
+    ) -> ExpandResult<Option<(Mark, Parse<T>)>>
+    where
+        F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
+    {
+        if self.recursion_depth == u32::MAX {
+            // Recursion limit has been reached somewhere in the macro expansion tree. We should
+            // stop expanding other macro calls in this tree, or else this may result in
+            // exponential number of macro expansions, leading to a hang.
+            //
+            // The overflow error should have been reported when it occurred (see the next branch),
+            // so don't return overflow error here to avoid diagnostics duplication.
+            cov_mark::hit!(overflow_but_not_me);
+            return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
+        } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
+            self.recursion_depth = u32::MAX;
+            cov_mark::hit!(your_stack_belongs_to_me);
+            return ExpandResult::only_err(ExpandError::Other(
+                "reached recursion limit during macro expansion".into(),
+            ));
+        }
+
+        let ExpandResult { value, err } = op(self);
+        let Some(call_id) = value else {
+            return ExpandResult { value: None, err };
+        };
+
+        Self::enter_expand_inner(db, call_id, err).map(|value| {
+            value.and_then(|InFile { file_id, value }| {
+                let parse = value.cast::<T>()?;
+
+                self.recursion_depth += 1;
+                self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
+                let old_file_id = std::mem::replace(&mut self.current_file_id, file_id);
+                let mark =
+                    Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
+                Some((mark, parse))
+            })
+        })
+    }
+}
+
+#[derive(Debug)]
+pub struct Mark {
+    file_id: HirFileId,
+    bomb: DropBomb,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 3f439232083..e8cc2eab461 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15;
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum PrefixKind {
     /// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
-    /// This is the same as plain, just that paths will start with `self` iprepended f the path
+    /// This is the same as plain, just that paths will start with `self` prepended if the path
     /// starts with an identifier that is not a crate.
     BySelf,
     /// Causes paths to ignore imports in the local module.
@@ -81,7 +81,7 @@ fn find_path_inner(
     }
 
     let def_map = from.def_map(db);
-    let crate_root = def_map.crate_root(db);
+    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();
@@ -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) in root_def_map.extern_prelude() {
         if module_id == def_id {
             let name = scope_name.unwrap_or_else(|| name.clone());
 
@@ -454,7 +454,7 @@ fn find_local_import_locations(
         worklist.push(ancestor);
     }
 
-    let def_map = def_map.crate_root(db).def_map(db);
+    let def_map = def_map.crate_root().def_map(db);
 
     let mut seen: FxHashSet<_> = FxHashSet::default();
 
@@ -543,6 +543,7 @@ mod tests {
                 module.local_id,
                 &mod_path,
                 crate::item_scope::BuiltinShadowMode::Module,
+                None,
             )
             .0
             .take_types()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index e4912fa8a64..f19c3f028f4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -12,16 +12,17 @@ use hir_expand::{
 use intern::Interned;
 use la_arena::{Arena, ArenaMap, Idx};
 use once_cell::unsync::Lazy;
-use std::ops::DerefMut;
 use stdx::impl_from;
 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
+use triomphe::Arc;
 
 use crate::{
-    body::{Expander, LowerCtx},
     child_by_source::ChildBySource,
     db::DefDatabase,
-    dyn_map::DynMap,
-    keys,
+    dyn_map::{keys, DynMap},
+    expander::Expander,
+    lower::LowerCtx,
+    nameres::{DefMap, MacroSubNs},
     src::{HasChildSource, HasSource},
     type_ref::{LifetimeRef, TypeBound, TypeRef},
     AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@@ -153,7 +154,6 @@ impl GenericParams {
         def: GenericDefId,
     ) -> Interned<GenericParams> {
         let _p = profile::span("generic_params_query");
-
         macro_rules! id_to_generics {
             ($id:ident) => {{
                 let id = $id.lookup(db).id;
@@ -176,8 +176,10 @@ impl GenericParams {
 
                 // Don't create an `Expander` nor call `loc.source(db)` if not needed since this
                 // causes a reparse after the `ItemTree` has been created.
-                let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
-                for (_, param) in &func_data.params {
+                let mut expander = Lazy::new(|| {
+                    (module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
+                });
+                for param in &func_data.params {
                     generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
                 }
 
@@ -329,7 +331,7 @@ impl GenericParams {
     pub(crate) fn fill_implicit_impl_trait_args(
         &mut self,
         db: &dyn DefDatabase,
-        expander: &mut impl DerefMut<Target = Expander>,
+        exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
         type_ref: &TypeRef,
     ) {
         type_ref.walk(&mut |type_ref| {
@@ -349,14 +351,28 @@ impl GenericParams {
             }
             if let TypeRef::Macro(mc) = type_ref {
                 let macro_call = mc.to_node(db.upcast());
-                match expander.enter_expand::<ast::Type>(db, macro_call) {
-                    Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
-                        let ctx = LowerCtx::new(db, expander.current_file_id());
-                        let type_ref = TypeRef::from_ast(&ctx, expanded);
-                        self.fill_implicit_impl_trait_args(db, expander, &type_ref);
-                        expander.exit(db, mark);
-                    }
-                    _ => {}
+                let (def_map, expander) = &mut **exp;
+
+                let module = expander.module.local_id;
+                let resolver = |path| {
+                    def_map
+                        .resolve_path(
+                            db,
+                            module,
+                            &path,
+                            crate::item_scope::BuiltinShadowMode::Other,
+                            Some(MacroSubNs::Bang),
+                        )
+                        .0
+                        .take_macros()
+                };
+                if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
+                    expander.enter_expand(db, macro_call, resolver)
+                {
+                    let ctx = expander.ctx(db);
+                    let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
+                    self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
+                    exp.1.exit(db, mark);
                 }
             }
         });
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 19fa6b25419..4ad8a7aa8eb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -12,26 +12,29 @@
 //!
 //! See also a neighboring `body` module.
 
+pub mod type_ref;
+
 use std::fmt;
 
 use hir_expand::name::Name;
 use intern::Interned;
 use la_arena::{Idx, RawIdx};
 use smallvec::SmallVec;
+use syntax::ast;
 
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    BlockId,
+    AnonymousConstId, BlockId,
 };
 
 pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
 
-pub type ExprId = Idx<Expr>;
-
 pub type BindingId = Idx<Binding>;
 
+pub type ExprId = Idx<Expr>;
+
 /// FIXME: this is a hacky function which should be removed
 pub(crate) fn dummy_expr_id() -> ExprId {
     ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper {
 pub enum Literal {
     String(Box<str>),
     ByteString(Box<[u8]>),
+    CString(Box<str>),
     Char(char),
     Bool(bool),
     Int(i128, Option<BuiltinInt>),
@@ -93,6 +97,66 @@ pub enum Literal {
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
+/// Used in range patterns.
+pub enum LiteralOrConst {
+    Literal(Literal),
+    Const(Path),
+}
+
+impl Literal {
+    pub fn negate(self) -> Option<Self> {
+        if let Literal::Int(i, k) = self {
+            Some(Literal::Int(-i, k))
+        } else {
+            None
+        }
+    }
+}
+
+impl From<ast::LiteralKind> for Literal {
+    fn from(ast_lit_kind: ast::LiteralKind) -> Self {
+        use ast::LiteralKind;
+        match ast_lit_kind {
+            // FIXME: these should have actual values filled in, but unsure on perf impact
+            LiteralKind::IntNumber(lit) => {
+                if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
+                    Literal::Float(
+                        FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
+                        builtin,
+                    )
+                } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
+                    Literal::Uint(lit.value().unwrap_or(0), builtin)
+                } else {
+                    let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
+                    Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+                }
+            }
+            LiteralKind::FloatNumber(lit) => {
+                let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
+                Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
+            }
+            LiteralKind::ByteString(bs) => {
+                let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
+                Literal::ByteString(text)
+            }
+            LiteralKind::String(s) => {
+                let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+                Literal::String(text)
+            }
+            LiteralKind::CString(s) => {
+                let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+                Literal::CString(text)
+            }
+            LiteralKind::Byte(b) => {
+                Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
+            }
+            LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
+            LiteralKind::Bool(val) => Literal::Bool(val),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Expr {
     /// This is produced if the syntax tree does not have a required expression piece.
     Missing,
@@ -107,28 +171,19 @@ pub enum Expr {
         expr: ExprId,
     },
     Block {
-        id: BlockId,
+        id: Option<BlockId>,
         statements: Box<[Statement]>,
         tail: Option<ExprId>,
         label: Option<LabelId>,
     },
-    TryBlock {
-        id: BlockId,
-        statements: Box<[Statement]>,
-        tail: Option<ExprId>,
-    },
     Async {
-        id: BlockId,
-        statements: Box<[Statement]>,
-        tail: Option<ExprId>,
-    },
-    Const {
-        id: BlockId,
+        id: Option<BlockId>,
         statements: Box<[Statement]>,
         tail: Option<ExprId>,
     },
+    Const(AnonymousConstId),
     Unsafe {
-        id: BlockId,
+        id: Option<BlockId>,
         statements: Box<[Statement]>,
         tail: Option<ExprId>,
     },
@@ -141,12 +196,6 @@ pub enum Expr {
         body: ExprId,
         label: Option<LabelId>,
     },
-    For {
-        iterable: ExprId,
-        pat: PatId,
-        body: ExprId,
-        label: Option<LabelId>,
-    },
     Call {
         callee: ExprId,
         args: Box<[ExprId]>,
@@ -163,11 +212,11 @@ pub enum Expr {
         arms: Box<[MatchArm]>,
     },
     Continue {
-        label: Option<Name>,
+        label: Option<LabelId>,
     },
     Break {
         expr: Option<ExprId>,
-        label: Option<Name>,
+        label: Option<LabelId>,
     },
     Return {
         expr: Option<ExprId>,
@@ -192,9 +241,6 @@ pub enum Expr {
     Await {
         expr: ExprId,
     },
-    Try {
-        expr: ExprId,
-    },
     Cast {
         expr: ExprId,
         type_ref: Interned<TypeRef>,
@@ -231,6 +277,7 @@ pub enum Expr {
         ret_type: Option<Interned<TypeRef>>,
         body: ExprId,
         closure_kind: ClosureKind,
+        capture_by: CaptureBy,
     },
     Tuple {
         exprs: Box<[ExprId]>,
@@ -249,6 +296,14 @@ pub enum ClosureKind {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureBy {
+    /// `move |x| y + x`.
+    Value,
+    /// `move` keyword was not specified.
+    Ref,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Movability {
     Static,
     Movable,
@@ -302,11 +357,10 @@ impl Expr {
             Expr::Let { expr, .. } => {
                 f(*expr);
             }
+            Expr::Const(_) => (),
             Expr::Block { statements, tail, .. }
-            | Expr::TryBlock { statements, tail, .. }
             | Expr::Unsafe { statements, tail, .. }
-            | Expr::Async { statements, tail, .. }
-            | Expr::Const { statements, tail, .. } => {
+            | Expr::Async { statements, tail, .. } => {
                 for stmt in statements.iter() {
                     match stmt {
                         Statement::Let { initializer, else_branch, .. } => {
@@ -329,10 +383,6 @@ impl Expr {
                 f(*condition);
                 f(*body);
             }
-            Expr::For { iterable, body, .. } => {
-                f(*iterable);
-                f(*body);
-            }
             Expr::Call { callee, args, .. } => {
                 f(*callee);
                 args.iter().copied().for_each(f);
@@ -383,7 +433,6 @@ impl Expr {
             }
             Expr::Field { expr, .. }
             | Expr::Await { expr }
-            | Expr::Try { expr }
             | Expr::Cast { expr, .. }
             | Expr::Ref { expr, .. }
             | Expr::UnaryOp { expr, .. }
@@ -438,10 +487,37 @@ impl BindingAnnotation {
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
+pub enum BindingProblems {
+    /// https://doc.rust-lang.org/stable/error_codes/E0416.html
+    BoundMoreThanOnce,
+    /// https://doc.rust-lang.org/stable/error_codes/E0409.html
+    BoundInconsistently,
+    /// https://doc.rust-lang.org/stable/error_codes/E0408.html
+    NotBoundAcrossAll,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Binding {
     pub name: Name,
     pub mode: BindingAnnotation,
     pub definitions: SmallVec<[PatId; 1]>,
+    /// Id of the closure/generator that owns this binding. If it is owned by the
+    /// top level expression, this field would be `None`.
+    pub owner: Option<ExprId>,
+    pub problems: Option<BindingProblems>,
+}
+
+impl Binding {
+    pub fn is_upvar(&self, relative_to: ExprId) -> bool {
+        match self.owner {
+            Some(x) => {
+                // We assign expression ids in a way that outer closures will receive
+                // a lower id
+                x.into_raw() < relative_to.into_raw()
+            }
+            None => true,
+        }
+    }
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -458,7 +534,7 @@ pub enum Pat {
     Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
     Or(Box<[PatId]>),
     Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
-    Range { start: ExprId, end: ExprId },
+    Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
     Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
     Path(Box<Path>),
     Lit(ExprId),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 8e30f429a9f..0573c9a6f8a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -1,9 +1,11 @@
 //! HIR for references to types. Paths in these are not yet resolved. They can
 //! be directly created from an ast::TypeRef, without further queries.
 
+use core::fmt;
 use std::fmt::Write;
 
 use hir_expand::{
+    db::ExpandDatabase,
     name::{AsName, Name},
     AstId,
 };
@@ -11,9 +13,9 @@ use intern::Interned;
 use syntax::ast::{self, HasName};
 
 use crate::{
-    body::LowerCtx,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
-    expr::Literal,
+    hir::Literal,
+    lower::LowerCtx,
     path::Path,
 };
 
@@ -383,15 +385,6 @@ pub enum ConstRefOrPath {
     Path(Name),
 }
 
-impl std::fmt::Display for ConstRefOrPath {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ConstRefOrPath::Scalar(s) => s.fmt(f),
-            ConstRefOrPath::Path(n) => n.fmt(f),
-        }
-    }
-}
-
 impl ConstRefOrPath {
     pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
         match expr {
@@ -400,6 +393,19 @@ impl ConstRefOrPath {
         }
     }
 
+    pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
+        struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRefOrPath);
+        impl fmt::Display for Display<'_> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                match self.1 {
+                    ConstRefOrPath::Scalar(s) => s.fmt(f),
+                    ConstRefOrPath::Path(n) => n.display(self.0).fmt(f),
+                }
+            }
+        }
+        Display(db, self)
+    }
+
     // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
     // parse stage.
     fn from_expr(expr: ast::Expr) -> Self {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 4f1f6000db0..48532655e04 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -1,6 +1,6 @@
 //! A map of all publicly exported items in a crate.
 
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
 
 use base_db::CrateId;
 use fst::{self, Streamer};
@@ -8,10 +8,11 @@ use hir_expand::name::Name;
 use indexmap::{map::Entry, IndexMap};
 use itertools::Itertools;
 use rustc_hash::{FxHashSet, FxHasher};
+use triomphe::Arc;
 
 use crate::{
-    db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
-    ModuleId, TraitId,
+    db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
+    ModuleDefId, ModuleId, TraitId,
 };
 
 type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -32,13 +33,23 @@ pub struct ImportPath {
     pub segments: Vec<Name>,
 }
 
-impl fmt::Display for ImportPath {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.segments.iter().format("::"), f)
+impl ImportPath {
+    pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a {
+        struct Display<'a> {
+            db: &'a dyn DefDatabase,
+            path: &'a ImportPath,
+        }
+        impl fmt::Display for Display<'_> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt::Display::fmt(
+                    &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"),
+                    f,
+                )
+            }
+        }
+        Display { db, path: self }
     }
-}
 
-impl ImportPath {
     fn len(&self) -> usize {
         self.segments.len()
     }
@@ -75,7 +86,7 @@ impl ImportMap {
         let mut importables = import_map
             .map
             .iter()
-            .map(|(item, info)| (item, fst_path(&info.path)))
+            .map(|(item, info)| (item, fst_path(db, &info.path)))
             .collect::<Vec<_>>();
         importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
 
@@ -112,6 +123,25 @@ impl ImportMap {
         self.map.get(&item)
     }
 
+    #[cfg(test)]
+    fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
+        let mut importable_paths: Vec<_> = self
+            .map
+            .iter()
+            .map(|(item, info)| {
+                let ns = match item {
+                    ItemInNs::Types(_) => "t",
+                    ItemInNs::Values(_) => "v",
+                    ItemInNs::Macros(_) => "m",
+                };
+                format!("- {} ({ns})", info.path.display(db))
+            })
+            .collect();
+
+        importable_paths.sort();
+        importable_paths.join("\n")
+    }
+
     fn collect_trait_assoc_items(
         &mut self,
         db: &dyn DefDatabase,
@@ -153,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
 
     // We look only into modules that are public(ly reexported), starting with the crate root.
     let empty = ImportPath { segments: vec![] };
-    let root = def_map.module_id(def_map.root());
+    let root = def_map.module_id(DefMap::ROOT);
     let mut worklist = vec![(root, empty)];
     while let Some((module, mod_path)) = worklist.pop() {
         let ext_def_map;
@@ -233,13 +263,10 @@ impl fmt::Debug for ImportMap {
         let mut importable_paths: Vec<_> = self
             .map
             .iter()
-            .map(|(item, info)| {
-                let ns = match item {
-                    ItemInNs::Types(_) => "t",
-                    ItemInNs::Values(_) => "v",
-                    ItemInNs::Macros(_) => "m",
-                };
-                format!("- {} ({ns})", info.path)
+            .map(|(item, _)| match item {
+                ItemInNs::Types(it) => format!("- {it:?} (t)",),
+                ItemInNs::Values(it) => format!("- {it:?} (v)",),
+                ItemInNs::Macros(it) => format!("- {it:?} (m)",),
             })
             .collect();
 
@@ -248,9 +275,9 @@ impl fmt::Debug for ImportMap {
     }
 }
 
-fn fst_path(path: &ImportPath) -> String {
+fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String {
     let _p = profile::span("fst_path");
-    let mut s = path.to_string();
+    let mut s = path.display(db).to_string();
     s.make_ascii_lowercase();
     s
 }
@@ -343,7 +370,12 @@ impl Query {
         self
     }
 
-    fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
+    fn import_matches(
+        &self,
+        db: &dyn DefDatabase,
+        import: &ImportInfo,
+        enforce_lowercase: bool,
+    ) -> bool {
         let _p = profile::span("import_map::Query::import_matches");
         if import.is_trait_assoc_item {
             if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
@@ -354,9 +386,9 @@ impl Query {
         }
 
         let mut input = if import.is_trait_assoc_item || self.name_only {
-            import.path.segments.last().unwrap().to_string()
+            import.path.segments.last().unwrap().display(db.upcast()).to_string()
         } else {
-            import.path.to_string()
+            import.path.display(db).to_string()
         };
         if enforce_lowercase || !self.case_sensitive {
             input.make_ascii_lowercase();
@@ -421,25 +453,27 @@ pub fn search_dependencies(
         let importables = &import_map.importables[indexed_value.value as usize..];
 
         let common_importable_data = &import_map.map[&importables[0]];
-        if !query.import_matches(common_importable_data, true) {
+        if !query.import_matches(db, common_importable_data, true) {
             continue;
         }
 
         // Path shared by the importable items in this group.
-        let common_importables_path_fst = fst_path(&common_importable_data.path);
+        let common_importables_path_fst = fst_path(db, &common_importable_data.path);
         // Add the items from this `ModPath` group. Those are all subsequent items in
         // `importables` whose paths match `path`.
         let iter = importables
             .iter()
             .copied()
-            .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
+            .take_while(|item| {
+                common_importables_path_fst == fst_path(db, &import_map.map[item].path)
+            })
             .filter(|&item| match item_import_kind(item) {
                 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
                 None => true,
             })
             .filter(|item| {
                 !query.case_sensitive // we've already checked the common importables path case-insensitively
-                        || query.import_matches(&import_map.map[item], false)
+                        || query.import_matches(db, &import_map.map[item], false)
             });
         res.extend(iter);
 
@@ -472,7 +506,7 @@ mod tests {
     use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
     use expect_test::{expect, Expect};
 
-    use crate::{test_db::TestDB, ItemContainerId, Lookup};
+    use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup};
 
     use super::*;
 
@@ -496,7 +530,7 @@ mod tests {
                 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
                     Some(assoc_item_path) => (assoc_item_path, "a"),
                     None => (
-                        dependency_imports.path_of(dependency)?.to_string(),
+                        dependency_imports.path_of(dependency)?.display(&db).to_string(),
                         match dependency {
                             ItemInNs::Types(ModuleDefId::FunctionId(_))
                             | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
@@ -547,7 +581,11 @@ mod tests {
                         None
                     }
                 })?;
-            return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?));
+            return Some(format!(
+                "{}::{}",
+                dependency_imports.path_of(trait_)?.display(db),
+                assoc_item_name.display(db.upcast())
+            ));
         }
         None
     }
@@ -587,7 +625,7 @@ mod tests {
 
                 let map = db.import_map(krate);
 
-                Some(format!("{name}:\n{map:?}\n"))
+                Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
             })
             .sorted()
             .collect::<String>();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 991e447033f..3ed321d189d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -4,7 +4,7 @@
 use std::collections::hash_map::Entry;
 
 use base_db::CrateId;
-use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId};
+use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
 use itertools::Itertools;
 use once_cell::sync::Lazy;
 use profile::Count;
@@ -358,12 +358,16 @@ impl ItemScope {
         }
     }
 
-    pub(crate) fn dump(&self, buf: &mut String) {
+    pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) {
         let mut entries: Vec<_> = self.resolutions().collect();
         entries.sort_by_key(|(name, _)| name.clone());
 
         for (name, def) in entries {
-            format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
+            format_to!(
+                buf,
+                "{}:",
+                name.map_or("_".to_string(), |name| name.display(db).to_string())
+            );
 
             if def.types.is_some() {
                 buf.push_str(" t");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 9da5b2d47c8..590ed64af5d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -40,7 +40,6 @@ use std::{
     hash::{Hash, Hasher},
     marker::PhantomData,
     ops::Index,
-    sync::Arc,
 };
 
 use ast::{AstNode, HasName, StructKind};
@@ -60,6 +59,7 @@ use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use stdx::never;
 use syntax::{ast, match_ast, SyntaxKind};
+use triomphe::Arc;
 
 use crate::{
     attr::Attrs,
@@ -101,16 +101,14 @@ pub struct ItemTree {
     top_level: SmallVec<[ModItem; 1]>,
     attrs: FxHashMap<AttrOwner, RawAttrs>,
 
+    // FIXME: Remove this indirection, an item tree is almost always non-empty?
     data: Option<Box<ItemTreeData>>,
 }
 
 impl ItemTree {
     pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
         let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
-        let syntax = match db.parse_or_expand(file_id) {
-            Some(node) => node,
-            None => return Default::default(),
-        };
+        let syntax = db.parse_or_expand(file_id);
         if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax)
         {
             // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
@@ -169,8 +167,8 @@ impl ItemTree {
         Attrs::filter(db, krate, self.raw_attrs(of).clone())
     }
 
-    pub fn pretty_print(&self) -> String {
-        pretty::print_item_tree(self)
+    pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
+        pretty::print_item_tree(db.upcast(), self)
     }
 
     fn data(&self) -> &ItemTreeData {
@@ -600,19 +598,18 @@ pub struct Function {
     pub abi: Option<Interned<str>>,
     pub params: IdxRange<Param>,
     pub ret_type: Interned<TypeRef>,
-    pub async_ret_type: Option<Interned<TypeRef>>,
     pub ast_id: FileAstId<ast::Fn>,
     pub(crate) flags: FnFlags,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Param {
-    Normal(Option<Name>, Interned<TypeRef>),
+    Normal(Interned<TypeRef>),
     Varargs,
 }
 
 bitflags::bitflags! {
-    #[derive(Default)]
+    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
     pub(crate) struct FnFlags: u8 {
         const HAS_SELF_PARAM = 1 << 0;
         const HAS_BODY = 1 << 1;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 77b186f8e3f..46633667ed3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -1,6 +1,6 @@
 //! AST -> `ItemTree` lowering code.
 
-use std::{collections::hash_map::Entry, sync::Arc};
+use std::collections::hash_map::Entry;
 
 use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
 use syntax::ast::{self, HasModuleItem, HasTypeBounds};
@@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> {
     db: &'a dyn DefDatabase,
     tree: ItemTree,
     source_ast_id_map: Arc<AstIdMap>,
-    body_ctx: crate::body::LowerCtx<'a>,
+    body_ctx: crate::lower::LowerCtx<'a>,
 }
 
 impl<'a> Ctx<'a> {
@@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
             db,
             tree: ItemTree::default(),
             source_ast_id_map: db.ast_id_map(file),
-            body_ctx: crate::body::LowerCtx::new(db, file),
+            body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
         }
     }
 
@@ -293,7 +293,7 @@ impl<'a> Ctx<'a> {
                     }
                 };
                 let ty = Interned::new(self_type);
-                let idx = self.data().params.alloc(Param::Normal(None, ty));
+                let idx = self.data().params.alloc(Param::Normal(ty));
                 self.add_attrs(
                     idx.into(),
                     RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@@ -306,19 +306,7 @@ impl<'a> Ctx<'a> {
                     None => {
                         let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
                         let ty = Interned::new(type_ref);
-                        let mut pat = param.pat();
-                        // FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
-                        // pattern names at all
-                        let name = 'name: loop {
-                            match pat {
-                                Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
-                                Some(ast::Pat::IdentPat(ident)) => {
-                                    break 'name ident.name().map(|it| it.as_name())
-                                }
-                                _ => break 'name None,
-                            }
-                        };
-                        self.data().params.alloc(Param::Normal(name, ty))
+                        self.data().params.alloc(Param::Normal(ty))
                     }
                 };
                 self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));
@@ -336,13 +324,12 @@ impl<'a> Ctx<'a> {
             None => TypeRef::unit(),
         };
 
-        let (ret_type, async_ret_type) = if func.async_token().is_some() {
-            let async_ret_type = ret_type.clone();
+        let ret_type = if func.async_token().is_some() {
             let future_impl = desugar_future_path(ret_type);
             let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
-            (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
+            TypeRef::ImplTrait(vec![ty_bound])
         } else {
-            (ret_type, None)
+            ret_type
         };
 
         let abi = func.abi().map(lower_abi);
@@ -376,7 +363,6 @@ impl<'a> Ctx<'a> {
             abi,
             params,
             ret_type: Interned::new(ret_type),
-            async_ret_type: async_ret_type.map(Interned::new),
             ast_id,
             flags,
         };
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 5f29997964b..e873316a578 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,6 +2,8 @@
 
 use std::fmt::{self, Write};
 
+use hir_expand::db::ExpandDatabase;
+
 use crate::{
     generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
     pretty::{print_path, print_type_bounds, print_type_ref},
@@ -10,8 +12,8 @@ use crate::{
 
 use super::*;
 
-pub(super) fn print_item_tree(tree: &ItemTree) -> String {
-    let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true };
+pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+    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);
@@ -43,6 +45,7 @@ macro_rules! wln {
 }
 
 struct Printer<'a> {
+    db: &'a dyn ExpandDatabase,
     tree: &'a ItemTree,
     buf: String,
     indent_level: usize,
@@ -88,7 +91,7 @@ impl<'a> Printer<'a> {
                 self,
                 "#{}[{}{}]",
                 inner,
-                attr.path,
+                attr.path.display(self.db),
                 attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
             );
         }
@@ -102,7 +105,7 @@ impl<'a> Printer<'a> {
 
     fn print_visibility(&mut self, vis: RawVisibilityId) {
         match &self.tree[vis] {
-            RawVisibility::Module(path) => w!(self, "pub({}) ", path),
+            RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
             RawVisibility::Public => w!(self, "pub "),
         };
     }
@@ -117,7 +120,7 @@ impl<'a> Printer<'a> {
                         let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
                         this.print_attrs_of(field);
                         this.print_visibility(*visibility);
-                        w!(this, "{}: ", name);
+                        w!(this, "{}: ", name.display(self.db));
                         this.print_type_ref(type_ref);
                         wln!(this, ",");
                     }
@@ -131,7 +134,7 @@ impl<'a> Printer<'a> {
                         let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
                         this.print_attrs_of(field);
                         this.print_visibility(*visibility);
-                        w!(this, "{}: ", name);
+                        w!(this, "{}: ", name.display(self.db));
                         this.print_type_ref(type_ref);
                         wln!(this, ",");
                     }
@@ -164,20 +167,20 @@ impl<'a> Printer<'a> {
     fn print_use_tree(&mut self, use_tree: &UseTree) {
         match &use_tree.kind {
             UseTreeKind::Single { path, alias } => {
-                w!(self, "{}", path);
+                w!(self, "{}", path.display(self.db));
                 if let Some(alias) = alias {
                     w!(self, " as {}", alias);
                 }
             }
             UseTreeKind::Glob { path } => {
                 if let Some(path) = path {
-                    w!(self, "{}::", path);
+                    w!(self, "{}::", path.display(self.db));
                 }
                 w!(self, "*");
             }
             UseTreeKind::Prefixed { prefix, list } => {
                 if let Some(prefix) = prefix {
-                    w!(self, "{}::", prefix);
+                    w!(self, "{}::", prefix.display(self.db));
                 }
                 w!(self, "{{");
                 for (i, tree) in list.iter().enumerate() {
@@ -205,7 +208,7 @@ impl<'a> Printer<'a> {
             ModItem::ExternCrate(it) => {
                 let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "extern crate {}", name);
+                w!(self, "extern crate {}", name.display(self.db));
                 if let Some(alias) = alias {
                     w!(self, " as {}", alias);
                 }
@@ -233,7 +236,6 @@ impl<'a> Printer<'a> {
                     abi,
                     params,
                     ret_type,
-                    async_ret_type: _,
                     ast_id: _,
                     flags,
                 } = &self.tree[it];
@@ -253,26 +255,20 @@ impl<'a> Printer<'a> {
                 if let Some(abi) = abi {
                     w!(self, "extern \"{}\" ", abi);
                 }
-                w!(self, "fn {}", name);
+                w!(self, "fn {}", name.display(self.db));
                 self.print_generic_params(explicit_generic_params);
                 w!(self, "(");
                 if !params.is_empty() {
                     self.indented(|this| {
-                        for (i, param) in params.clone().enumerate() {
+                        for param in params.clone() {
                             this.print_attrs_of(param);
                             match &this.tree[param] {
-                                Param::Normal(name, ty) => {
-                                    match name {
-                                        Some(name) => w!(this, "{}: ", name),
-                                        None => w!(this, "_: "),
+                                Param::Normal(ty) => {
+                                    if flags.contains(FnFlags::HAS_SELF_PARAM) {
+                                        w!(this, "self: ");
                                     }
                                     this.print_type_ref(ty);
-                                    w!(this, ",");
-                                    if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
-                                        wln!(this, "  // self");
-                                    } else {
-                                        wln!(this);
-                                    }
+                                    wln!(this, ",");
                                 }
                                 Param::Varargs => {
                                     wln!(this, "...");
@@ -293,7 +289,7 @@ impl<'a> Printer<'a> {
             ModItem::Struct(it) => {
                 let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "struct {}", name);
+                w!(self, "struct {}", name.display(self.db));
                 self.print_generic_params(generic_params);
                 self.print_fields_and_where_clause(fields, generic_params);
                 if matches!(fields, Fields::Record(_)) {
@@ -305,7 +301,7 @@ impl<'a> Printer<'a> {
             ModItem::Union(it) => {
                 let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "union {}", name);
+                w!(self, "union {}", name.display(self.db));
                 self.print_generic_params(generic_params);
                 self.print_fields_and_where_clause(fields, generic_params);
                 if matches!(fields, Fields::Record(_)) {
@@ -317,14 +313,14 @@ impl<'a> Printer<'a> {
             ModItem::Enum(it) => {
                 let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "enum {}", name);
+                w!(self, "enum {}", name.display(self.db));
                 self.print_generic_params(generic_params);
                 self.print_where_clause_and_opening_brace(generic_params);
                 self.indented(|this| {
                     for variant in variants.clone() {
                         let Variant { name, fields, ast_id: _ } = &this.tree[variant];
                         this.print_attrs_of(variant);
-                        w!(this, "{}", name);
+                        w!(this, "{}", name.display(self.db));
                         this.print_fields(fields);
                         wln!(this, ",");
                     }
@@ -336,7 +332,7 @@ impl<'a> Printer<'a> {
                 self.print_visibility(*visibility);
                 w!(self, "const ");
                 match name {
-                    Some(name) => w!(self, "{}", name),
+                    Some(name) => w!(self, "{}", name.display(self.db)),
                     None => w!(self, "_"),
                 }
                 w!(self, ": ");
@@ -350,7 +346,7 @@ impl<'a> Printer<'a> {
                 if *mutable {
                     w!(self, "mut ");
                 }
-                w!(self, "{}: ", name);
+                w!(self, "{}: ", name.display(self.db));
                 self.print_type_ref(type_ref);
                 w!(self, " = _;");
                 wln!(self);
@@ -372,7 +368,7 @@ impl<'a> Printer<'a> {
                 if *is_auto {
                     w!(self, "auto ");
                 }
-                w!(self, "trait {}", name);
+                w!(self, "trait {}", name.display(self.db));
                 self.print_generic_params(generic_params);
                 self.print_where_clause_and_opening_brace(generic_params);
                 self.indented(|this| {
@@ -385,7 +381,7 @@ impl<'a> Printer<'a> {
             ModItem::TraitAlias(it) => {
                 let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "trait {}", name);
+                w!(self, "trait {}", name.display(self.db));
                 self.print_generic_params(generic_params);
                 w!(self, " = ");
                 self.print_where_clause(generic_params);
@@ -418,7 +414,7 @@ impl<'a> Printer<'a> {
                 let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
                     &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "type {}", name);
+                w!(self, "type {}", name.display(self.db));
                 self.print_generic_params(generic_params);
                 if !bounds.is_empty() {
                     w!(self, ": ");
@@ -435,7 +431,7 @@ impl<'a> Printer<'a> {
             ModItem::Mod(it) => {
                 let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "mod {}", name);
+                w!(self, "mod {}", name.display(self.db));
                 match kind {
                     ModKind::Inline { items } => {
                         w!(self, " {{");
@@ -453,16 +449,16 @@ impl<'a> Printer<'a> {
             }
             ModItem::MacroCall(it) => {
                 let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
-                wln!(self, "{}!(...);", path);
+                wln!(self, "{}!(...);", path.display(self.db));
             }
             ModItem::MacroRules(it) => {
                 let MacroRules { name, ast_id: _ } = &self.tree[it];
-                wln!(self, "macro_rules! {} {{ ... }}", name);
+                wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
             }
             ModItem::MacroDef(it) => {
                 let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                wln!(self, "macro {} {{ ... }}", name);
+                wln!(self, "macro {} {{ ... }}", name.display(self.db));
             }
         }
 
@@ -470,15 +466,15 @@ impl<'a> Printer<'a> {
     }
 
     fn print_type_ref(&mut self, type_ref: &TypeRef) {
-        print_type_ref(type_ref, self).unwrap();
+        print_type_ref(self.db, type_ref, self).unwrap();
     }
 
     fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
-        print_type_bounds(bounds, self).unwrap();
+        print_type_bounds(self.db, bounds, self).unwrap();
     }
 
     fn print_path(&mut self, path: &Path) {
-        print_path(path, self).unwrap();
+        print_path(self.db, path, self).unwrap();
     }
 
     fn print_generic_params(&mut self, params: &GenericParams) {
@@ -493,7 +489,7 @@ impl<'a> Printer<'a> {
                 w!(self, ", ");
             }
             first = false;
-            w!(self, "{}", lt.name);
+            w!(self, "{}", lt.name.display(self.db));
         }
         for (idx, x) in params.type_or_consts.iter() {
             if !first {
@@ -502,11 +498,11 @@ impl<'a> Printer<'a> {
             first = false;
             match x {
                 TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
-                    Some(name) => w!(self, "{}", name),
+                    Some(name) => w!(self, "{}", name.display(self.db)),
                     None => w!(self, "_anon_{}", idx.into_raw()),
                 },
                 TypeOrConstParamData::ConstParamData(konst) => {
-                    w!(self, "const {}: ", konst.name);
+                    w!(self, "const {}: ", konst.name.display(self.db));
                     self.print_type_ref(&konst.ty);
                 }
             }
@@ -538,7 +534,12 @@ impl<'a> Printer<'a> {
                 let (target, bound) = match pred {
                     WherePredicate::TypeBound { target, bound } => (target, bound),
                     WherePredicate::Lifetime { target, bound } => {
-                        wln!(this, "{}: {},", target.name, bound.name);
+                        wln!(
+                            this,
+                            "{}: {},",
+                            target.name.display(self.db),
+                            bound.name.display(self.db)
+                        );
                         continue;
                     }
                     WherePredicate::ForLifetime { lifetimes, target, bound } => {
@@ -547,7 +548,7 @@ impl<'a> Printer<'a> {
                             if i != 0 {
                                 w!(this, ", ");
                             }
-                            w!(this, "{}", lt);
+                            w!(this, "{}", lt.display(self.db));
                         }
                         w!(this, "> ");
                         (target, bound)
@@ -558,7 +559,7 @@ impl<'a> Printer<'a> {
                     WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
                     WherePredicateTypeTarget::TypeOrConstParam(id) => {
                         match &params.type_or_consts[*id].name() {
-                            Some(name) => w!(this, "{}", name),
+                            Some(name) => w!(this, "{}", name.display(self.db)),
                             None => w!(this, "_anon_{}", id.into_raw()),
                         }
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index e30d9652bb5..5ded4b6b273 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
 fn check(ra_fixture: &str, expect: Expect) {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     let item_tree = db.file_item_tree(file_id.into());
-    let pretty = item_tree.pretty_print();
+    let pretty = item_tree.pretty_print(&db);
     expect.assert_eq(&pretty);
 }
 
@@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
     fn method(&self);
 }
         "#,
-        expect![[r##"
+        expect![[r#"
             pub static mut ST: () = _;
 
             pub(self) const _: Anon = _;
@@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
             #[inner_attr_in_fn]
             pub(self) fn f(
                 #[attr]
-                arg: u8,
-                _: (),
+                u8,
+                (),
             ) -> () { ... }
 
             pub(self) trait Tr<Self>
@@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
                 pub(self) type Assoc: AssocBound = Default;
 
                 pub(self) fn method(
-                    _: &Self,  // self
+                    self: &Self,
                 ) -> ();
             }
-        "##]],
+        "#]],
     );
 }
 
@@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
                 T: 'b
             {
                 pub(self) fn f<G>(
-                    arg: impl Copy,
+                    impl Copy,
                 ) -> impl Copy
                 where
                     G: 'a { ... }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index d338bb41205..0e9ac58fbaa 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -2,14 +2,13 @@
 //!
 //! This attribute to tell the compiler about semi built-in std library
 //! features, such as Fn family of traits.
-use std::sync::Arc;
-
 use rustc_hash::FxHashMap;
 use syntax::SmolStr;
+use triomphe::Arc;
 
 use crate::{
-    db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
-    ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+    db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
+    FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
 };
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -200,7 +199,7 @@ pub enum GenericRequirement {
 
 macro_rules! language_item_table {
     (
-        $( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )*
+        $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )*
     ) => {
 
         /// A representation of all the valid language items in Rust.
@@ -221,11 +220,6 @@ macro_rules! language_item_table {
             }
 
             /// Opposite of [`LangItem::name`]
-            pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
-                Self::from_str(name.as_str()?)
-            }
-
-            /// Opposite of [`LangItem::name`]
             pub fn from_str(name: &str) -> Option<Self> {
                 match name {
                     $( stringify!($name) => Some(LangItem::$variant), )*
@@ -236,84 +230,100 @@ macro_rules! language_item_table {
     }
 }
 
+impl LangItem {
+    /// Opposite of [`LangItem::name`]
+    pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
+        Self::from_str(name.as_str()?)
+    }
+
+    pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
+        let t = db.lang_item(start_crate, *self)?;
+        Some(Path::LangItem(t))
+    }
+}
+
 language_item_table! {
 //  Variant name,            Name,                     Getter method name,         Target                  Generic requirements;
-    Sized,                   sized,               sized_trait,                Target::Trait,          GenericRequirement::Exact(0);
-    Unsize,                  unsize,              unsize_trait,               Target::Trait,          GenericRequirement::Minimum(1);
+    Sized,                   sym::sized,               sized_trait,                Target::Trait,          GenericRequirement::Exact(0);
+    Unsize,                  sym::unsize,              unsize_trait,               Target::Trait,          GenericRequirement::Minimum(1);
     /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
-    StructuralPeq,           structural_peq,      structural_peq_trait,       Target::Trait,          GenericRequirement::None;
+    StructuralPeq,           sym::structural_peq,      structural_peq_trait,       Target::Trait,          GenericRequirement::None;
     /// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
-    StructuralTeq,           structural_teq,      structural_teq_trait,       Target::Trait,          GenericRequirement::None;
-    Copy,                    copy,                copy_trait,                 Target::Trait,          GenericRequirement::Exact(0);
-    Clone,                   clone,               clone_trait,                Target::Trait,          GenericRequirement::None;
-    Sync,                    sync,                sync_trait,                 Target::Trait,          GenericRequirement::Exact(0);
-    DiscriminantKind,        discriminant_kind,   discriminant_kind_trait,    Target::Trait,          GenericRequirement::None;
+    StructuralTeq,           sym::structural_teq,      structural_teq_trait,       Target::Trait,          GenericRequirement::None;
+    Copy,                    sym::copy,                copy_trait,                 Target::Trait,          GenericRequirement::Exact(0);
+    Clone,                   sym::clone,               clone_trait,                Target::Trait,          GenericRequirement::None;
+    Sync,                    sym::sync,                sync_trait,                 Target::Trait,          GenericRequirement::Exact(0);
+    DiscriminantKind,        sym::discriminant_kind,   discriminant_kind_trait,    Target::Trait,          GenericRequirement::None;
     /// The associated item of the [`DiscriminantKind`] trait.
-    Discriminant,            discriminant_type,   discriminant_type,          Target::AssocTy,        GenericRequirement::None;
+    Discriminant,            sym::discriminant_type,   discriminant_type,          Target::AssocTy,        GenericRequirement::None;
+
+    PointeeTrait,            sym::pointee_trait,       pointee_trait,              Target::Trait,          GenericRequirement::None;
+    Metadata,                sym::metadata_type,       metadata_type,              Target::AssocTy,        GenericRequirement::None;
+    DynMetadata,             sym::dyn_metadata,        dyn_metadata,               Target::Struct,         GenericRequirement::None;
 
-    PointeeTrait,            pointee_trait,       pointee_trait,              Target::Trait,          GenericRequirement::None;
-    Metadata,                metadata_type,       metadata_type,              Target::AssocTy,        GenericRequirement::None;
-    DynMetadata,             dyn_metadata,        dyn_metadata,               Target::Struct,         GenericRequirement::None;
+    Freeze,                  sym::freeze,              freeze_trait,               Target::Trait,          GenericRequirement::Exact(0);
 
-    Freeze,                  freeze,              freeze_trait,               Target::Trait,          GenericRequirement::Exact(0);
+    FnPtrTrait,              sym::fn_ptr_trait,        fn_ptr_trait,               Target::Trait,          GenericRequirement::Exact(0);
+    FnPtrAddr,               sym::fn_ptr_addr,         fn_ptr_addr,                Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
 
-    Drop,                    drop,                drop_trait,                 Target::Trait,          GenericRequirement::None;
-    Destruct,                destruct,            destruct_trait,             Target::Trait,          GenericRequirement::None;
+    Drop,                    sym::drop,                drop_trait,                 Target::Trait,          GenericRequirement::None;
+    Destruct,                sym::destruct,            destruct_trait,             Target::Trait,          GenericRequirement::None;
 
-    CoerceUnsized,           coerce_unsized,      coerce_unsized_trait,       Target::Trait,          GenericRequirement::Minimum(1);
-    DispatchFromDyn,         dispatch_from_dyn,   dispatch_from_dyn_trait,    Target::Trait,          GenericRequirement::Minimum(1);
+    CoerceUnsized,           sym::coerce_unsized,      coerce_unsized_trait,       Target::Trait,          GenericRequirement::Minimum(1);
+    DispatchFromDyn,         sym::dispatch_from_dyn,   dispatch_from_dyn_trait,    Target::Trait,          GenericRequirement::Minimum(1);
 
     // language items relating to transmutability
-    TransmuteOpts,           transmute_opts,      transmute_opts,             Target::Struct,         GenericRequirement::Exact(0);
-    TransmuteTrait,          transmute_trait,     transmute_trait,            Target::Trait,          GenericRequirement::Exact(3);
-
-    Add,                     add,                 add_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    Sub,                     sub,                 sub_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    Mul,                     mul,                 mul_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    Div,                     div,                 div_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    Rem,                     rem,                 rem_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    Neg,                     neg,                 neg_trait,                  Target::Trait,          GenericRequirement::Exact(0);
-    Not,                     not,                 not_trait,                  Target::Trait,          GenericRequirement::Exact(0);
-    BitXor,                  bitxor,              bitxor_trait,               Target::Trait,          GenericRequirement::Exact(1);
-    BitAnd,                  bitand,              bitand_trait,               Target::Trait,          GenericRequirement::Exact(1);
-    BitOr,                   bitor,               bitor_trait,                Target::Trait,          GenericRequirement::Exact(1);
-    Shl,                     shl,                 shl_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    Shr,                     shr,                 shr_trait,                  Target::Trait,          GenericRequirement::Exact(1);
-    AddAssign,               add_assign,          add_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    SubAssign,               sub_assign,          sub_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    MulAssign,               mul_assign,          mul_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    DivAssign,               div_assign,          div_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    RemAssign,               rem_assign,          rem_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    BitXorAssign,            bitxor_assign,       bitxor_assign_trait,        Target::Trait,          GenericRequirement::Exact(1);
-    BitAndAssign,            bitand_assign,       bitand_assign_trait,        Target::Trait,          GenericRequirement::Exact(1);
-    BitOrAssign,             bitor_assign,        bitor_assign_trait,         Target::Trait,          GenericRequirement::Exact(1);
-    ShlAssign,               shl_assign,          shl_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    ShrAssign,               shr_assign,          shr_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
-    Index,                   index,               index_trait,                Target::Trait,          GenericRequirement::Exact(1);
-    IndexMut,                index_mut,           index_mut_trait,            Target::Trait,          GenericRequirement::Exact(1);
-
-    UnsafeCell,              unsafe_cell,         unsafe_cell_type,           Target::Struct,         GenericRequirement::None;
-    VaList,                  va_list,             va_list,                    Target::Struct,         GenericRequirement::None;
-
-    Deref,                   deref,               deref_trait,                Target::Trait,          GenericRequirement::Exact(0);
-    DerefMut,                deref_mut,           deref_mut_trait,            Target::Trait,          GenericRequirement::Exact(0);
-    DerefTarget,             deref_target,        deref_target,               Target::AssocTy,        GenericRequirement::None;
-    Receiver,                receiver,            receiver_trait,             Target::Trait,          GenericRequirement::None;
-
-    Fn,                      fn,                  fn_trait,                   Target::Trait,          GenericRequirement::Exact(1);
-    FnMut,                   fn_mut,              fn_mut_trait,               Target::Trait,          GenericRequirement::Exact(1);
-    FnOnce,                  fn_once,             fn_once_trait,              Target::Trait,          GenericRequirement::Exact(1);
-
-    FnOnceOutput,            fn_once_output,      fn_once_output,             Target::AssocTy,        GenericRequirement::None;
-
-    Future,                  future_trait,        future_trait,               Target::Trait,          GenericRequirement::Exact(0);
-    GeneratorState,          generator_state,     gen_state,                  Target::Enum,           GenericRequirement::None;
-    Generator,               generator,           gen_trait,                  Target::Trait,          GenericRequirement::Minimum(1);
-    Unpin,                   unpin,               unpin_trait,                Target::Trait,          GenericRequirement::None;
-    Pin,                     pin,                 pin_type,                   Target::Struct,         GenericRequirement::None;
-
-    PartialEq,               eq,                  eq_trait,                   Target::Trait,          GenericRequirement::Exact(1);
-    PartialOrd,              partial_ord,         partial_ord_trait,          Target::Trait,          GenericRequirement::Exact(1);
+    TransmuteOpts,           sym::transmute_opts,      transmute_opts,             Target::Struct,         GenericRequirement::Exact(0);
+    TransmuteTrait,          sym::transmute_trait,     transmute_trait,            Target::Trait,          GenericRequirement::Exact(3);
+
+    Add,                     sym::add,                 add_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    Sub,                     sym::sub,                 sub_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    Mul,                     sym::mul,                 mul_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    Div,                     sym::div,                 div_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    Rem,                     sym::rem,                 rem_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    Neg,                     sym::neg,                 neg_trait,                  Target::Trait,          GenericRequirement::Exact(0);
+    Not,                     sym::not,                 not_trait,                  Target::Trait,          GenericRequirement::Exact(0);
+    BitXor,                  sym::bitxor,              bitxor_trait,               Target::Trait,          GenericRequirement::Exact(1);
+    BitAnd,                  sym::bitand,              bitand_trait,               Target::Trait,          GenericRequirement::Exact(1);
+    BitOr,                   sym::bitor,               bitor_trait,                Target::Trait,          GenericRequirement::Exact(1);
+    Shl,                     sym::shl,                 shl_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    Shr,                     sym::shr,                 shr_trait,                  Target::Trait,          GenericRequirement::Exact(1);
+    AddAssign,               sym::add_assign,          add_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    SubAssign,               sym::sub_assign,          sub_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    MulAssign,               sym::mul_assign,          mul_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    DivAssign,               sym::div_assign,          div_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    RemAssign,               sym::rem_assign,          rem_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    BitXorAssign,            sym::bitxor_assign,       bitxor_assign_trait,        Target::Trait,          GenericRequirement::Exact(1);
+    BitAndAssign,            sym::bitand_assign,       bitand_assign_trait,        Target::Trait,          GenericRequirement::Exact(1);
+    BitOrAssign,             sym::bitor_assign,        bitor_assign_trait,         Target::Trait,          GenericRequirement::Exact(1);
+    ShlAssign,               sym::shl_assign,          shl_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    ShrAssign,               sym::shr_assign,          shr_assign_trait,           Target::Trait,          GenericRequirement::Exact(1);
+    Index,                   sym::index,               index_trait,                Target::Trait,          GenericRequirement::Exact(1);
+    IndexMut,                sym::index_mut,           index_mut_trait,            Target::Trait,          GenericRequirement::Exact(1);
+
+    UnsafeCell,              sym::unsafe_cell,         unsafe_cell_type,           Target::Struct,         GenericRequirement::None;
+    VaList,                  sym::va_list,             va_list,                    Target::Struct,         GenericRequirement::None;
+
+    Deref,                   sym::deref,               deref_trait,                Target::Trait,          GenericRequirement::Exact(0);
+    DerefMut,                sym::deref_mut,           deref_mut_trait,            Target::Trait,          GenericRequirement::Exact(0);
+    DerefTarget,             sym::deref_target,        deref_target,               Target::AssocTy,        GenericRequirement::None;
+    Receiver,                sym::receiver,            receiver_trait,             Target::Trait,          GenericRequirement::None;
+
+    Fn,                      kw::fn,                   fn_trait,                   Target::Trait,          GenericRequirement::Exact(1);
+    FnMut,                   sym::fn_mut,              fn_mut_trait,               Target::Trait,          GenericRequirement::Exact(1);
+    FnOnce,                  sym::fn_once,             fn_once_trait,              Target::Trait,          GenericRequirement::Exact(1);
+
+    FnOnceOutput,            sym::fn_once_output,      fn_once_output,             Target::AssocTy,        GenericRequirement::None;
+
+    Future,                  sym::future_trait,        future_trait,               Target::Trait,          GenericRequirement::Exact(0);
+    GeneratorState,          sym::generator_state,     gen_state,                  Target::Enum,           GenericRequirement::None;
+    Generator,               sym::generator,           gen_trait,                  Target::Trait,          GenericRequirement::Minimum(1);
+    Unpin,                   sym::unpin,               unpin_trait,                Target::Trait,          GenericRequirement::None;
+    Pin,                     sym::pin,                 pin_type,                   Target::Struct,         GenericRequirement::None;
+
+    PartialEq,               sym::eq,                  eq_trait,                   Target::Trait,          GenericRequirement::Exact(1);
+    PartialOrd,              sym::partial_ord,         partial_ord_trait,          Target::Trait,          GenericRequirement::Exact(1);
+    CVoid,                   sym::c_void,              c_void,                     Target::Enum,           GenericRequirement::None;
 
     // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
     // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
@@ -322,92 +332,103 @@ language_item_table! {
     // in the sense that a crate is not required to have it defined to use it, but a final product
     // is required to define it somewhere. Additionally, there are restrictions on crates that use
     // a weak lang item, but do not have it defined.
-    Panic,                   panic,               panic_fn,                   Target::Fn,             GenericRequirement::Exact(0);
-    PanicNounwind,           panic_nounwind,      panic_nounwind,             Target::Fn,             GenericRequirement::Exact(0);
-    PanicFmt,                panic_fmt,           panic_fmt,                  Target::Fn,             GenericRequirement::None;
-    PanicDisplay,            panic_display,       panic_display,              Target::Fn,             GenericRequirement::None;
-    ConstPanicFmt,           const_panic_fmt,     const_panic_fmt,            Target::Fn,             GenericRequirement::None;
-    PanicBoundsCheck,        panic_bounds_check,  panic_bounds_check_fn,      Target::Fn,             GenericRequirement::Exact(0);
-    PanicInfo,               panic_info,          panic_info,                 Target::Struct,         GenericRequirement::None;
-    PanicLocation,           panic_location,      panic_location,             Target::Struct,         GenericRequirement::None;
-    PanicImpl,               panic_impl,          panic_impl,                 Target::Fn,             GenericRequirement::None;
-    PanicCannotUnwind,       panic_cannot_unwind, panic_cannot_unwind,        Target::Fn,             GenericRequirement::Exact(0);
+    Panic,                   sym::panic,               panic_fn,                   Target::Fn,             GenericRequirement::Exact(0);
+    PanicNounwind,           sym::panic_nounwind,      panic_nounwind,             Target::Fn,             GenericRequirement::Exact(0);
+    PanicFmt,                sym::panic_fmt,           panic_fmt,                  Target::Fn,             GenericRequirement::None;
+    PanicDisplay,            sym::panic_display,       panic_display,              Target::Fn,             GenericRequirement::None;
+    ConstPanicFmt,           sym::const_panic_fmt,     const_panic_fmt,            Target::Fn,             GenericRequirement::None;
+    PanicBoundsCheck,        sym::panic_bounds_check,  panic_bounds_check_fn,      Target::Fn,             GenericRequirement::Exact(0);
+    PanicMisalignedPointerDereference,        sym::panic_misaligned_pointer_dereference,  panic_misaligned_pointer_dereference_fn,      Target::Fn,             GenericRequirement::Exact(0);
+    PanicInfo,               sym::panic_info,          panic_info,                 Target::Struct,         GenericRequirement::None;
+    PanicLocation,           sym::panic_location,      panic_location,             Target::Struct,         GenericRequirement::None;
+    PanicImpl,               sym::panic_impl,          panic_impl,                 Target::Fn,             GenericRequirement::None;
+    PanicCannotUnwind,       sym::panic_cannot_unwind, panic_cannot_unwind,        Target::Fn,             GenericRequirement::Exact(0);
     /// libstd panic entry point. Necessary for const eval to be able to catch it
-    BeginPanic,              begin_panic,         begin_panic_fn,             Target::Fn,             GenericRequirement::None;
+    BeginPanic,              sym::begin_panic,         begin_panic_fn,             Target::Fn,             GenericRequirement::None;
 
-    ExchangeMalloc,          exchange_malloc,     exchange_malloc_fn,         Target::Fn,             GenericRequirement::None;
-    BoxFree,                 box_free,            box_free_fn,                Target::Fn,             GenericRequirement::Minimum(1);
-    DropInPlace,             drop_in_place,       drop_in_place_fn,           Target::Fn,             GenericRequirement::Minimum(1);
-    AllocLayout,             alloc_layout,        alloc_layout,               Target::Struct,         GenericRequirement::None;
+    // Lang items needed for `format_args!()`.
+    FormatAlignment,         sym::format_alignment,    format_alignment,           Target::Enum,           GenericRequirement::None;
+    FormatArgument,          sym::format_argument,     format_argument,            Target::Struct,         GenericRequirement::None;
+    FormatArguments,         sym::format_arguments,    format_arguments,           Target::Struct,         GenericRequirement::None;
+    FormatCount,             sym::format_count,        format_count,               Target::Enum,           GenericRequirement::None;
+    FormatPlaceholder,       sym::format_placeholder,  format_placeholder,         Target::Struct,         GenericRequirement::None;
+    FormatUnsafeArg,         sym::format_unsafe_arg,   format_unsafe_arg,          Target::Struct,         GenericRequirement::None;
 
-    Start,                   start,               start_fn,                   Target::Fn,             GenericRequirement::Exact(1);
+    ExchangeMalloc,          sym::exchange_malloc,     exchange_malloc_fn,         Target::Fn,             GenericRequirement::None;
+    BoxFree,                 sym::box_free,            box_free_fn,                Target::Fn,             GenericRequirement::Minimum(1);
+    DropInPlace,             sym::drop_in_place,       drop_in_place_fn,           Target::Fn,             GenericRequirement::Minimum(1);
+    AllocLayout,             sym::alloc_layout,        alloc_layout,               Target::Struct,         GenericRequirement::None;
 
-    EhPersonality,           eh_personality,      eh_personality,             Target::Fn,             GenericRequirement::None;
-    EhCatchTypeinfo,         eh_catch_typeinfo,   eh_catch_typeinfo,          Target::Static,         GenericRequirement::None;
+    Start,                   sym::start,               start_fn,                   Target::Fn,             GenericRequirement::Exact(1);
 
-    OwnedBox,                owned_box,           owned_box,                  Target::Struct,         GenericRequirement::Minimum(1);
+    EhPersonality,           sym::eh_personality,      eh_personality,             Target::Fn,             GenericRequirement::None;
+    EhCatchTypeinfo,         sym::eh_catch_typeinfo,   eh_catch_typeinfo,          Target::Static,         GenericRequirement::None;
 
-    PhantomData,             phantom_data,        phantom_data,               Target::Struct,         GenericRequirement::Exact(1);
+    OwnedBox,                sym::owned_box,           owned_box,                  Target::Struct,         GenericRequirement::Minimum(1);
 
-    ManuallyDrop,            manually_drop,       manually_drop,              Target::Struct,         GenericRequirement::None;
+    PhantomData,             sym::phantom_data,        phantom_data,               Target::Struct,         GenericRequirement::Exact(1);
 
-    MaybeUninit,             maybe_uninit,        maybe_uninit,               Target::Union,          GenericRequirement::None;
+    ManuallyDrop,            sym::manually_drop,       manually_drop,              Target::Struct,         GenericRequirement::None;
+
+    MaybeUninit,             sym::maybe_uninit,        maybe_uninit,               Target::Union,          GenericRequirement::None;
 
     /// Align offset for stride != 1; must not panic.
-    AlignOffset,             align_offset,        align_offset_fn,            Target::Fn,             GenericRequirement::None;
+    AlignOffset,             sym::align_offset,        align_offset_fn,            Target::Fn,             GenericRequirement::None;
 
-    Termination,             termination,         termination,                Target::Trait,          GenericRequirement::None;
+    Termination,             sym::termination,         termination,                Target::Trait,          GenericRequirement::None;
 
-    Try,                     Try,                 try_trait,                  Target::Trait,          GenericRequirement::None;
+    Try,                     sym::Try,                 try_trait,                  Target::Trait,          GenericRequirement::None;
 
-    Tuple,                   tuple_trait,         tuple_trait,                Target::Trait,          GenericRequirement::Exact(0);
+    Tuple,                   sym::tuple_trait,         tuple_trait,                Target::Trait,          GenericRequirement::Exact(0);
 
-    SliceLen,                slice_len_fn,        slice_len_fn,               Target::Method(MethodKind::Inherent), GenericRequirement::None;
+    SliceLen,                sym::slice_len_fn,        slice_len_fn,               Target::Method(MethodKind::Inherent), GenericRequirement::None;
 
     // Language items from AST lowering
-    TryTraitFromResidual,    from_residual,       from_residual_fn,           Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
-    TryTraitFromOutput,      from_output,         from_output_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
-    TryTraitBranch,          branch,              branch_fn,                  Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
-    TryTraitFromYeet,        from_yeet,           from_yeet_fn,               Target::Fn,             GenericRequirement::None;
+    TryTraitFromResidual,    sym::from_residual,       from_residual_fn,           Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    TryTraitFromOutput,      sym::from_output,         from_output_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    TryTraitBranch,          sym::branch,              branch_fn,                  Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    TryTraitFromYeet,        sym::from_yeet,           from_yeet_fn,               Target::Fn,             GenericRequirement::None;
+
+    PointerLike,             sym::pointer_like,        pointer_like,               Target::Trait,          GenericRequirement::Exact(0);
 
-    PointerSized,            pointer_sized,       pointer_sized,              Target::Trait,          GenericRequirement::Exact(0);
+    ConstParamTy,            sym::const_param_ty,      const_param_ty_trait,       Target::Trait,          GenericRequirement::Exact(0);
 
-    Poll,                    Poll,                poll,                       Target::Enum,           GenericRequirement::None;
-    PollReady,               Ready,               poll_ready_variant,         Target::Variant,        GenericRequirement::None;
-    PollPending,             Pending,             poll_pending_variant,       Target::Variant,        GenericRequirement::None;
+    Poll,                    sym::Poll,                poll,                       Target::Enum,           GenericRequirement::None;
+    PollReady,               sym::Ready,               poll_ready_variant,         Target::Variant,        GenericRequirement::None;
+    PollPending,             sym::Pending,             poll_pending_variant,       Target::Variant,        GenericRequirement::None;
 
     // FIXME(swatinem): the following lang items are used for async lowering and
     // should become obsolete eventually.
-    ResumeTy,                ResumeTy,            resume_ty,                  Target::Struct,         GenericRequirement::None;
-    GetContext,              get_context,         get_context_fn,             Target::Fn,             GenericRequirement::None;
-
-    Context,                 Context,             context,                    Target::Struct,         GenericRequirement::None;
-    FuturePoll,              poll,                future_poll_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    ResumeTy,                sym::ResumeTy,            resume_ty,                  Target::Struct,         GenericRequirement::None;
+    GetContext,              sym::get_context,         get_context_fn,             Target::Fn,             GenericRequirement::None;
 
-    FromFrom,                from,                from_fn,                    Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    Context,                 sym::Context,             context,                    Target::Struct,         GenericRequirement::None;
+    FuturePoll,              sym::poll,                future_poll_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
 
-    OptionSome,              Some,                option_some_variant,        Target::Variant,        GenericRequirement::None;
-    OptionNone,              None,                option_none_variant,        Target::Variant,        GenericRequirement::None;
+    Option,                  sym::Option,              option_type,                Target::Enum,           GenericRequirement::None;
+    OptionSome,              sym::Some,                option_some_variant,        Target::Variant,        GenericRequirement::None;
+    OptionNone,              sym::None,                option_none_variant,        Target::Variant,        GenericRequirement::None;
 
-    ResultOk,                Ok,                  result_ok_variant,          Target::Variant,        GenericRequirement::None;
-    ResultErr,               Err,                 result_err_variant,         Target::Variant,        GenericRequirement::None;
+    ResultOk,                sym::Ok,                  result_ok_variant,          Target::Variant,        GenericRequirement::None;
+    ResultErr,               sym::Err,                 result_err_variant,         Target::Variant,        GenericRequirement::None;
 
-    ControlFlowContinue,     Continue,            cf_continue_variant,        Target::Variant,        GenericRequirement::None;
-    ControlFlowBreak,        Break,               cf_break_variant,           Target::Variant,        GenericRequirement::None;
+    ControlFlowContinue,     sym::Continue,            cf_continue_variant,        Target::Variant,        GenericRequirement::None;
+    ControlFlowBreak,        sym::Break,               cf_break_variant,           Target::Variant,        GenericRequirement::None;
 
-    IntoFutureIntoFuture,    into_future,         into_future_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
-    IntoIterIntoIter,        into_iter,           into_iter_fn,               Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
-    IteratorNext,            next,                next_fn,                    Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
+    IntoFutureIntoFuture,    sym::into_future,         into_future_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    IntoIterIntoIter,        sym::into_iter,           into_iter_fn,               Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    IteratorNext,            sym::next,                next_fn,                    Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
 
-    PinNewUnchecked,         new_unchecked,       new_unchecked_fn,           Target::Method(MethodKind::Inherent), GenericRequirement::None;
+    PinNewUnchecked,         sym::new_unchecked,       new_unchecked_fn,           Target::Method(MethodKind::Inherent), GenericRequirement::None;
 
-    RangeFrom,               RangeFrom,           range_from_struct,          Target::Struct,         GenericRequirement::None;
-    RangeFull,               RangeFull,           range_full_struct,          Target::Struct,         GenericRequirement::None;
-    RangeInclusiveStruct,    RangeInclusive,      range_inclusive_struct,     Target::Struct,         GenericRequirement::None;
-    RangeInclusiveNew,       range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
-    Range,                   Range,               range_struct,               Target::Struct,         GenericRequirement::None;
-    RangeToInclusive,        RangeToInclusive,    range_to_inclusive_struct,  Target::Struct,         GenericRequirement::None;
-    RangeTo,                 RangeTo,             range_to_struct,            Target::Struct,         GenericRequirement::None;
+    RangeFrom,               sym::RangeFrom,           range_from_struct,          Target::Struct,         GenericRequirement::None;
+    RangeFull,               sym::RangeFull,           range_full_struct,          Target::Struct,         GenericRequirement::None;
+    RangeInclusiveStruct,    sym::RangeInclusive,      range_inclusive_struct,     Target::Struct,         GenericRequirement::None;
+    RangeInclusiveNew,       sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+    Range,                   sym::Range,               range_struct,               Target::Struct,         GenericRequirement::None;
+    RangeToInclusive,        sym::RangeToInclusive,    range_to_inclusive_struct,  Target::Struct,         GenericRequirement::None;
+    RangeTo,                 sym::RangeTo,             range_to_struct,            Target::Struct,         GenericRequirement::None;
 
-    String,                  String,              string,                     Target::Struct,         GenericRequirement::None;
+    String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
+    CStr,                    sym::CStr,                c_str,                      Target::Struct,         GenericRequirement::None;
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs b/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
deleted file mode 100644
index 49b1190ad46..00000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-//! Definitions needed for computing data layout of types.
-
-use std::cmp;
-
-use la_arena::{Idx, RawIdx};
-pub use rustc_abi::{
-    Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
-    LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
-    TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
-};
-
-use crate::LocalEnumVariantId;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
-
-impl rustc_index::vec::Idx for RustcEnumVariantIdx {
-    fn new(idx: usize) -> Self {
-        RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
-    }
-
-    fn index(self) -> usize {
-        u32::from(self.0.into_raw()) as usize
-    }
-}
-
-pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
-pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
-pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
-
-pub trait IntegerExt {
-    fn repr_discr(
-        dl: &TargetDataLayout,
-        repr: &ReprOptions,
-        min: i128,
-        max: i128,
-    ) -> Result<(Integer, bool), LayoutError>;
-}
-
-impl IntegerExt for Integer {
-    /// Finds the appropriate Integer type and signedness for the given
-    /// signed discriminant range and `#[repr]` attribute.
-    /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
-    /// that shouldn't affect anything, other than maybe debuginfo.
-    fn repr_discr(
-        dl: &TargetDataLayout,
-        repr: &ReprOptions,
-        min: i128,
-        max: i128,
-    ) -> Result<(Integer, bool), LayoutError> {
-        // Theoretically, negative values could be larger in unsigned representation
-        // than the unsigned representation of the signed minimum. However, if there
-        // are any negative values, the only valid unsigned representation is u128
-        // which can fit all i128 values, so the result remains unaffected.
-        let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
-        let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
-
-        if let Some(ity) = repr.int {
-            let discr = Integer::from_attr(dl, ity);
-            let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
-            if discr < fit {
-                return Err(LayoutError::UserError(
-                    "Integer::repr_discr: `#[repr]` hint too small for \
-                      discriminant range of enum "
-                        .to_string(),
-                ));
-            }
-            return Ok((discr, ity.is_signed()));
-        }
-
-        let at_least = if repr.c() {
-            // This is usually I32, however it can be different on some platforms,
-            // notably hexagon and arm-none/thumb-none
-            dl.c_enum_min_size
-        } else {
-            // repr(Rust) enums try to be as small as possible
-            Integer::I8
-        };
-
-        // If there are no negative values, we can use the unsigned fit.
-        Ok(if min >= 0 {
-            (cmp::max(unsigned_fit, at_least), false)
-        } else {
-            (cmp::max(signed_fit, at_least), true)
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum LayoutError {
-    UserError(String),
-    SizeOverflow,
-    TargetLayoutNotAvailable,
-    HasPlaceholder,
-    NotImplemented,
-    Unknown,
-}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 8c2e93f0905..9cd3dfd6f7c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -18,24 +18,23 @@ pub mod db;
 
 pub mod attr;
 pub mod path;
-pub mod type_ref;
 pub mod builtin_type;
-pub mod builtin_attr;
 pub mod per_ns;
 pub mod item_scope;
 
+pub mod lower;
+pub mod expander;
+
 pub mod dyn_map;
-pub mod keys;
 
 pub mod item_tree;
 
-pub mod adt;
 pub mod data;
 pub mod generics;
 pub mod lang_item;
-pub mod layout;
 
-pub mod expr;
+pub mod hir;
+pub use self::hir::type_ref;
 pub mod body;
 pub mod resolver;
 
@@ -49,29 +48,34 @@ pub mod visibility;
 pub mod find_path;
 pub mod import_map;
 
+pub use rustc_abi as layout;
+use triomphe::Arc;
+
 #[cfg(test)]
 mod test_db;
 #[cfg(test)]
 mod macro_expansion_tests;
 mod pretty;
 
-use std::{
-    hash::{Hash, Hasher},
-    sync::Arc,
-};
+use std::hash::{Hash, Hasher};
 
-use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
+use base_db::{
+    impl_intern_key,
+    salsa::{self, InternId},
+    CrateId, ProcMacroKind,
+};
 use hir_expand::{
     ast_id_map::FileAstId,
     attrs::{Attr, AttrId, AttrInput},
     builtin_attr_macro::BuiltinAttrExpander,
     builtin_derive_macro::BuiltinDeriveExpander,
     builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
-    eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
+    db::ExpandDatabase,
+    eager::expand_eager_macro,
     hygiene::Hygiene,
     proc_macro::ProcMacroExpander,
-    AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
-    MacroDefKind, UnresolvedMacro,
+    AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
+    MacroDefId, MacroDefKind, UnresolvedMacro,
 };
 use item_tree::ExternBlock;
 use la_arena::Idx;
@@ -82,8 +86,8 @@ use syntax::ast;
 use ::tt::token_id as tt;
 
 use crate::{
-    adt::VariantData,
     builtin_type::BuiltinType,
+    data::adt::VariantData,
     item_tree::{
         Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
         Static, Struct, Trait, TraitAlias, TypeAlias, Union,
@@ -104,13 +108,7 @@ pub struct ModuleId {
 impl ModuleId {
     pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
         match self.block {
-            Some(block) => {
-                db.block_def_map(block).unwrap_or_else(|| {
-                    // NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s,
-                    // so the `DefMap` here must exist.
-                    unreachable!("no `block_def_map` for `ModuleId` {:?}", self);
-                })
-            }
+            Some(block) => db.block_def_map(block),
             None => db.crate_def_map(self.krate),
         }
     }
@@ -236,7 +234,7 @@ pub struct EnumVariantId {
     pub local_id: LocalEnumVariantId,
 }
 
-pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
+pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct FieldId {
@@ -244,7 +242,7 @@ pub struct FieldId {
     pub local_id: LocalFieldId,
 }
 
-pub type LocalFieldId = Idx<adt::FieldData>;
+pub type LocalFieldId = Idx<data::adt::FieldData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ConstId(salsa::InternId);
@@ -478,6 +476,46 @@ impl_from!(
     for ModuleDefId
 );
 
+// FIXME: make this a DefWithBodyId
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct AnonymousConstId(InternId);
+impl_intern_key!(AnonymousConstId);
+
+/// A constant, which might appears as a const item, an annonymous const block in expressions
+/// or patterns, or as a constant in types with const generics.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum GeneralConstId {
+    ConstId(ConstId),
+    AnonymousConstId(AnonymousConstId),
+}
+
+impl_from!(ConstId, AnonymousConstId for GeneralConstId);
+
+impl GeneralConstId {
+    pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option<GenericDefId> {
+        match self {
+            GeneralConstId::ConstId(x) => Some(x.into()),
+            GeneralConstId::AnonymousConstId(x) => {
+                let (parent, _) = db.lookup_intern_anonymous_const(x);
+                parent.as_generic_def_id()
+            }
+        }
+    }
+
+    pub fn name(self, db: &dyn db::DefDatabase) -> String {
+        match self {
+            GeneralConstId::ConstId(const_id) => db
+                .const_data(const_id)
+                .name
+                .as_ref()
+                .and_then(|x| x.as_str())
+                .unwrap_or("_")
+                .to_owned(),
+            GeneralConstId::AnonymousConstId(id) => format!("{{anonymous const {id:?}}}"),
+        }
+    }
+}
+
 /// The defs which have a body.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum DefWithBodyId {
@@ -799,52 +837,43 @@ impl AttrDefId {
 pub trait AsMacroCall {
     fn as_call_id(
         &self,
-        db: &dyn db::DefDatabase,
+        db: &dyn ExpandDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
     ) -> Option<MacroCallId> {
-        self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok()
+        self.as_call_id_with_errors(db, krate, resolver).ok()?.value
     }
 
     fn as_call_id_with_errors(
         &self,
-        db: &dyn db::DefDatabase,
+        db: &dyn ExpandDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-        error_sink: &mut dyn FnMut(ExpandError),
-    ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
+    ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
 }
 
 impl AsMacroCall for InFile<&ast::MacroCall> {
     fn as_call_id_with_errors(
         &self,
-        db: &dyn db::DefDatabase,
+        db: &dyn ExpandDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-        mut error_sink: &mut dyn FnMut(ExpandError),
-    ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+    ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
         let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
         let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
-        let h = Hygiene::new(db.upcast(), self.file_id);
-        let path =
-            self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
-
-        let path = match error_sink
-            .option(path, || ExpandError::Other("malformed macro invocation".into()))
-        {
-            Ok(path) => path,
-            Err(error) => {
-                return Ok(Err(error));
-            }
+        let h = Hygiene::new(db, self.file_id);
+        let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
+
+        let Some(path) = path else {
+            return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
         };
 
-        macro_call_as_call_id(
+        macro_call_as_call_id_(
             db,
             &AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
             expands_to,
             krate,
             resolver,
-            error_sink,
         )
     }
 }
@@ -863,26 +892,37 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
 }
 
 fn macro_call_as_call_id(
-    db: &dyn db::DefDatabase,
+    db: &dyn ExpandDatabase,
+    call: &AstIdWithPath<ast::MacroCall>,
+    expand_to: ExpandTo,
+    krate: CrateId,
+    resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
+) -> Result<Option<MacroCallId>, UnresolvedMacro> {
+    macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
+}
+
+fn macro_call_as_call_id_(
+    db: &dyn ExpandDatabase,
     call: &AstIdWithPath<ast::MacroCall>,
     expand_to: ExpandTo,
     krate: CrateId,
     resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-    error_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
     let def =
         resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
 
     let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
-        let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
-
-        expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
+        let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
+        expand_eager_macro(db, krate, macro_call, def, &resolver)?
     } else {
-        Ok(def.as_lazy_macro(
-            db.upcast(),
-            krate,
-            MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
-        ))
+        ExpandResult {
+            value: Some(def.as_lazy_macro(
+                db,
+                krate,
+                MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
+            )),
+            err: None,
+        }
     };
     Ok(res)
 }
@@ -986,7 +1026,6 @@ fn attr_macro_as_call_id(
     macro_attr: &Attr,
     krate: CrateId,
     def: MacroDefId,
-    is_derive: bool,
 ) -> MacroCallId {
     let arg = match macro_attr.input.as_deref() {
         Some(AttrInput::TokenTree(tt, map)) => (
@@ -1007,7 +1046,6 @@ fn attr_macro_as_call_id(
             ast_id: item_attr.ast_id,
             attr_args: Arc::new(arg),
             invoc_attr_index: macro_attr.id,
-            is_derive,
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
new file mode 100644
index 00000000000..af623fd0e5d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
@@ -0,0 +1,45 @@
+//! Context for lowering paths.
+use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile};
+use once_cell::unsync::OnceCell;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{db::DefDatabase, path::Path};
+
+pub struct LowerCtx<'a> {
+    pub db: &'a dyn DefDatabase,
+    hygiene: Hygiene,
+    ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
+}
+
+impl<'a> LowerCtx<'a> {
+    pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
+        LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
+    }
+
+    pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+        LowerCtx {
+            db,
+            hygiene: Hygiene::new(db.upcast(), file_id),
+            ast_id_map: Some((file_id, OnceCell::new())),
+        }
+    }
+
+    pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
+        LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
+    }
+
+    pub(crate) fn hygiene(&self) -> &Hygiene {
+        &self.hygiene
+    }
+
+    pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
+        Path::from_src(ast, self)
+    }
+
+    pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
+        let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
+        let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
+        Some(InFile::new(file_id, ast_id_map.ast_id(item)))
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index fafcde25ae7..80474bc154d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -16,7 +16,7 @@ struct Foo;
 #[derive(Copy)]
 struct Foo;
 
-impl < > core::marker::Copy for Foo< > {}"#]],
+impl < > core::marker::Copy for Foo< > where {}"#]],
     );
 }
 
@@ -41,7 +41,7 @@ macro Copy {}
 #[derive(Copy)]
 struct Foo;
 
-impl < > crate ::marker::Copy for Foo< > {}"#]],
+impl < > crate ::marker::Copy for Foo< > where {}"#]],
     );
 }
 
@@ -57,7 +57,7 @@ struct Foo<A, B>;
 #[derive(Copy)]
 struct Foo<A, B>;
 
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
     );
 }
 
@@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
 #[derive(Copy)]
 struct Foo<A, B, 'a, 'b>;
 
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
     );
 }
 
@@ -84,13 +84,33 @@ fn test_clone_expand() {
         r#"
 //- minicore: derive, clone
 #[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+    Move { x: A, y: B },
+    Do(&'static str),
+    Jump,
+}
 "#,
         expect![[r#"
 #[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+    Move { x: A, y: B },
+    Do(&'static str),
+    Jump,
+}
 
-impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where {
+    fn clone(&self ) -> Self {
+        match self {
+            Command::Move {
+                x: x, y: y,
+            }
+            =>Command::Move {
+                x: x.clone(), y: y.clone(),
+            }
+            , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump,
+        }
+    }
+}"#]],
     );
 }
 
@@ -106,6 +126,270 @@ struct Foo<const X: usize, T>(u32);
 #[derive(Clone)]
 struct Foo<const X: usize, T>(u32);
 
-impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {
+    fn clone(&self ) -> Self {
+        match self {
+            Foo(f0, )=>Foo(f0.clone(), ),
+        }
+    }
+}"#]],
+    );
+}
+
+#[test]
+fn test_default_expand() {
+    check(
+        r#"
+//- minicore: derive, default
+#[derive(Default)]
+struct Foo {
+    field1: i32,
+    field2: (),
+}
+#[derive(Default)]
+enum Bar {
+    Foo(u8),
+    #[default]
+    Bar,
+}
+"#,
+        expect![[r#"
+#[derive(Default)]
+struct Foo {
+    field1: i32,
+    field2: (),
+}
+#[derive(Default)]
+enum Bar {
+    Foo(u8),
+    #[default]
+    Bar,
+}
+
+impl < > core::default::Default for Foo< > where {
+    fn default() -> Self {
+        Foo {
+            field1: core::default::Default::default(), field2: core::default::Default::default(),
+        }
+    }
+}
+impl < > core::default::Default for Bar< > where {
+    fn default() -> Self {
+        Bar::Bar
+    }
+}"#]],
+    );
+}
+
+#[test]
+fn test_partial_eq_expand() {
+    check(
+        r#"
+//- minicore: derive, eq
+#[derive(PartialEq, Eq)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+"#,
+        expect![[r#"
+#[derive(PartialEq, Eq)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+
+impl < > core::cmp::PartialEq for Command< > where {
+    fn eq(&self , other: &Self ) -> bool {
+        match (self , other) {
+            (Command::Move {
+                x: x_self, y: y_self,
+            }
+            , Command::Move {
+                x: x_other, y: y_other,
+            }
+            )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false
+        }
+    }
+}
+impl < > core::cmp::Eq for Command< > where {}"#]],
+    );
+}
+
+#[test]
+fn test_partial_ord_expand() {
+    check(
+        r#"
+//- minicore: derive, ord
+#[derive(PartialOrd, Ord)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+"#,
+        expect![[r#"
+#[derive(PartialOrd, Ord)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+
+impl < > core::cmp::PartialOrd for Command< > where {
+    fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> {
+        match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
+            core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+                match (self , other) {
+                    (Command::Move {
+                        x: x_self, y: y_self,
+                    }
+                    , Command::Move {
+                        x: x_other, y: y_other,
+                    }
+                    )=>match x_self.partial_cmp(&x_other) {
+                        core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+                            match y_self.partial_cmp(&y_other) {
+                                core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+                                    core::option::Option::Some(core::cmp::Ordering::Equal)
+                                }
+                                c=>return c,
+                            }
+                        }
+                        c=>return c,
+                    }
+                    , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
+                        core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+                            core::option::Option::Some(core::cmp::Ordering::Equal)
+                        }
+                        c=>return c,
+                    }
+                    , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
+                }
+            }
+            c=>return c,
+        }
+    }
+}
+impl < > core::cmp::Ord for Command< > where {
+    fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
+        match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
+            core::cmp::Ordering::Equal=> {
+                match (self , other) {
+                    (Command::Move {
+                        x: x_self, y: y_self,
+                    }
+                    , Command::Move {
+                        x: x_other, y: y_other,
+                    }
+                    )=>match x_self.cmp(&x_other) {
+                        core::cmp::Ordering::Equal=> {
+                            match y_self.cmp(&y_other) {
+                                core::cmp::Ordering::Equal=> {
+                                    core::cmp::Ordering::Equal
+                                }
+                                c=>return c,
+                            }
+                        }
+                        c=>return c,
+                    }
+                    , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
+                        core::cmp::Ordering::Equal=> {
+                            core::cmp::Ordering::Equal
+                        }
+                        c=>return c,
+                    }
+                    , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
+                }
+            }
+            c=>return c,
+        }
+    }
+}"#]],
+    );
+}
+
+#[test]
+fn test_hash_expand() {
+    check(
+        r#"
+//- minicore: derive, hash
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+"#,
+        expect![[r#"
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+
+impl < > core::hash::Hash for Command< > where {
+    fn hash<H: core::hash::Hasher>(&self , state: &mut H) {
+        core::mem::discriminant(self ).hash(state);
+        match self {
+            Command::Move {
+                x: x, y: y,
+            }
+            => {
+                x.hash(state);
+                y.hash(state);
+            }
+            , Command::Do(f0, )=> {
+                f0.hash(state);
+            }
+            , Command::Jump=> {}
+            ,
+        }
+    }
+}"#]],
+    );
+}
+
+#[test]
+fn test_debug_expand() {
+    check(
+        r#"
+//- minicore: derive, fmt
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+"#,
+        expect![[r#"
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+    Move { x: i32, y: i32 },
+    Do(&'static str),
+    Jump,
+}
+
+impl < > core::fmt::Debug for Command< > where {
+    fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        match self {
+            Command::Move {
+                x: x, y: y,
+            }
+            =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"),
+        }
+    }
+}"#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 5fbd1789b3a..977f300636f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -13,12 +13,12 @@ macro_rules! column {() => {}}
 
 fn main() { column!(); }
 "#,
-        expect![[r##"
+        expect![[r#"
 #[rustc_builtin_macro]
 macro_rules! column {() => {}}
 
-fn main() { 0; }
-"##]],
+fn main() { 0 as u32; }
+"#]],
     );
 }
 
@@ -31,12 +31,12 @@ macro_rules! line {() => {}}
 
 fn main() { line!() }
 "#,
-        expect![[r##"
+        expect![[r#"
 #[rustc_builtin_macro]
 macro_rules! line {() => {}}
 
-fn main() { 0 }
-"##]],
+fn main() { 0 as u32 }
+"#]],
     );
 }
 
@@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
 #[rustc_builtin_macro]
 macro_rules! option_env {() => {}}
 
-fn main() { $crate::option::Option::None:: < &str>; }
+fn main() { ::core::option::Option::None:: < &str>; }
 "#]],
     );
 }
@@ -193,7 +193,7 @@ fn main() {
     format_args!("{} {:?}", arg1(a, b, c), arg2);
 }
 "#,
-        expect![[r#"
+        expect![[r##"
 #[rustc_builtin_macro]
 macro_rules! format_args {
     ($fmt:expr) => ({ /* compiler built-in */ });
@@ -201,9 +201,9 @@ macro_rules! format_args {
 }
 
 fn main() {
-    $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]);
+    ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]);
 }
-"#]],
+"##]],
     );
 }
 
@@ -221,7 +221,7 @@ fn main() {
     format_args!("{} {:?}", a::<A,B>(), b);
 }
 "#,
-        expect![[r#"
+        expect![[r##"
 #[rustc_builtin_macro]
 macro_rules! format_args {
     ($fmt:expr) => ({ /* compiler built-in */ });
@@ -229,9 +229,76 @@ macro_rules! format_args {
 }
 
 fn main() {
-    $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]);
+    ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
 }
-"#]],
+"##]],
+    );
+}
+
+#[test]
+fn test_format_args_expand_with_raw_strings() {
+    check(
+        r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    format_args!(
+        r#"{},mismatch,"{}","{}""#,
+        location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+        mismatch.expected.display(db),
+        mismatch.actual.display(db)
+    );
+}
+"##,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
+}
+"##]],
+    );
+}
+
+#[test]
+fn test_format_args_expand_eager() {
+    check(
+        r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b);
+}
+"#,
+        expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+    ($fmt:expr) => ({ /* compiler built-in */ });
+    ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+    ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
+}
+"##]],
     );
 }
 
@@ -250,7 +317,7 @@ fn main() {
         format_args!/*+errors*/("{} {:?}", a.);
 }
 "#,
-        expect![[r#"
+        expect![[r##"
 #[rustc_builtin_macro]
 macro_rules! format_args {
     ($fmt:expr) => ({ /* compiler built-in */ });
@@ -259,10 +326,10 @@ macro_rules! format_args {
 
 fn main() {
     let _ =
-        /* parse error: expected field name or number */
-$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]);
+        /* error: no rule matches input tokens *//* parse error: expected field name or number */
+::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]);
 }
-"#]],
+"##]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 7a3e8c3b05c..553ffe3d0b8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -4,6 +4,7 @@
 mod tt_conversion;
 mod matching;
 mod meta_syntax;
+mod metavar_expr;
 mod regression;
 
 use expect_test::expect;
@@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22
     );
 }
 #[test]
-fn float_field_acces_macro_input() {
+fn float_field_access_macro_input() {
     check(
         r#"
 macro_rules! foo {
@@ -922,7 +923,7 @@ macro_rules! m {
 
 fn bar() -> &'a Baz<u8> {}
 
-fn bar() -> extern "Rust"fn() -> Ret {}
+fn bar() -> extern "Rust" fn() -> Ret {}
 "#]],
     );
 }
@@ -1293,20 +1294,54 @@ ok!();
 }
 
 #[test]
-fn test_vertical_bar_with_pat() {
+fn test_vertical_bar_with_pat_param() {
     check(
         r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
 m! { |x| }
  "#,
         expect![[r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
 ok!();
  "#]],
     );
 }
 
 #[test]
+fn test_new_std_matches() {
+    check(
+        r#"
+macro_rules! matches {
+    ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+        match $expression {
+            $pattern $(if $guard)? => true,
+            _ => false
+        }
+    };
+}
+fn main() {
+    matches!(0, 0 | 1 if true);
+}
+ "#,
+        expect![[r#"
+macro_rules! matches {
+    ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+        match $expression {
+            $pattern $(if $guard)? => true,
+            _ => false
+        }
+    };
+}
+fn main() {
+    match 0 {
+        0|1 if true =>true , _=>false
+    };
+}
+ "#]],
+    );
+}
+
+#[test]
 fn test_dollar_crate_lhs_is_not_meta() {
     check(
         r#"
@@ -1581,92 +1616,6 @@ struct Foo;
 }
 
 #[test]
-fn test_dollar_dollar() {
-    check(
-        r#"
-macro_rules! register_struct { ($Struct:ident) => {
-    macro_rules! register_methods { ($$($method:ident),*) => {
-        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
-            struct $Struct;
-            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
-        }}
-    }}
-}}
-
-register_struct!(Foo);
-register_methods!(alpha, beta);
-implement_methods!(1, 2, 3);
-"#,
-        expect![[r#"
-macro_rules! register_struct { ($Struct:ident) => {
-    macro_rules! register_methods { ($$($method:ident),*) => {
-        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
-            struct $Struct;
-            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
-        }}
-    }}
-}}
-
-macro_rules !register_methods {
-    ($($method: ident), *) = > {
-        macro_rules!implement_methods {
-            ($$($val: expr), *) = > {
-                struct Foo;
-                impl Foo {
-                    $(fn $method()-> &'static[u32] {
-                        &[$$($$val), *]
-                    }
-                    )*
-                }
-            }
-        }
-    }
-}
-macro_rules !implement_methods {
-    ($($val: expr), *) = > {
-        struct Foo;
-        impl Foo {
-            fn alpha()-> &'static[u32] {
-                &[$($val), *]
-            }
-            fn beta()-> &'static[u32] {
-                &[$($val), *]
-            }
-        }
-    }
-}
-struct Foo;
-impl Foo {
-    fn alpha() -> &'static[u32] {
-        &[1, 2, 3]
-    }
-    fn beta() -> &'static[u32] {
-        &[1, 2, 3]
-    }
-}
-"#]],
-    )
-}
-
-#[test]
-fn test_metavar_exprs() {
-    check(
-        r#"
-macro_rules! m {
-    ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = m!(a b c);
-    "#,
-        expect![[r#"
-macro_rules! m {
-    ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = -0--1--2;
-    "#]],
-    );
-}
-
-#[test]
 fn test_punct_without_space() {
     // Puncts are "glued" greedily.
     check(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 26f16542cbb..0909d8c8354 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -33,7 +33,7 @@ m!(&k");
 "#,
         expect![[r#"
 macro_rules! m { ($i:literal) => {}; }
-/* error: Failed to lower macro args to token tree */"#]],
+/* error: invalid token tree */"#]],
     );
 }
 
@@ -73,7 +73,7 @@ fn main() {
 macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }
 
 fn main() {
-    let a = 2let b = 5drop(b-a)println!("{}", a+b)
+    let a = 2 let b = 5 drop(b-a)println!("{}", a+b)
 }
 "#]],
     )
@@ -106,7 +106,6 @@ stringify!(;
 
 #[test]
 fn range_patterns() {
-    // FIXME: rustc thinks there are three patterns here, not one.
     check(
         r#"
 macro_rules! m {
@@ -118,7 +117,7 @@ m!(.. .. ..);
 macro_rules! m {
     ($($p:pat)*) => (stringify!($($p |)*);)
 }
-stringify!(.. .. .. |);
+stringify!(.. | .. | .. |);
 "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
new file mode 100644
index 00000000000..967b5ad36ba
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
@@ -0,0 +1,311 @@
+//! Tests for RFC 3086 metavariable expressions.
+
+use expect_test::expect;
+
+use crate::macro_expansion_tests::check;
+
+#[test]
+fn test_dollar_dollar() {
+    check(
+        r#"
+macro_rules! register_struct { ($Struct:ident) => {
+    macro_rules! register_methods { ($$($method:ident),*) => {
+        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+            struct $Struct;
+            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+        }}
+    }}
+}}
+
+register_struct!(Foo);
+register_methods!(alpha, beta);
+implement_methods!(1, 2, 3);
+"#,
+        expect![[r#"
+macro_rules! register_struct { ($Struct:ident) => {
+    macro_rules! register_methods { ($$($method:ident),*) => {
+        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+            struct $Struct;
+            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+        }}
+    }}
+}}
+
+macro_rules !register_methods {
+    ($($method: ident), *) = > {
+        macro_rules!implement_methods {
+            ($$($val: expr), *) = > {
+                struct Foo;
+                impl Foo {
+                    $(fn $method()-> &'static[u32] {
+                        &[$$($$val), *]
+                    }
+                    )*
+                }
+            }
+        }
+    }
+}
+macro_rules !implement_methods {
+    ($($val: expr), *) = > {
+        struct Foo;
+        impl Foo {
+            fn alpha()-> &'static[u32] {
+                &[$($val), *]
+            }
+            fn beta()-> &'static[u32] {
+                &[$($val), *]
+            }
+        }
+    }
+}
+struct Foo;
+impl Foo {
+    fn alpha() -> &'static[u32] {
+        &[1, 2, 3]
+    }
+    fn beta() -> &'static[u32] {
+        &[1, 2, 3]
+    }
+}
+"#]],
+    )
+}
+
+#[test]
+fn test_metavar_exprs() {
+    check(
+        r#"
+macro_rules! m {
+    ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = m!(a b c);
+    "#,
+        expect![[r#"
+macro_rules! m {
+    ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = -0--1--2;
+    "#]],
+    );
+}
+
+#[test]
+fn count_basic() {
+    check(
+        r#"
+macro_rules! m {
+    ($($t:ident),*) => {
+        ${count(t)}
+    }
+}
+
+fn test() {
+    m!();
+    m!(a);
+    m!(a, a);
+}
+"#,
+        expect![[r#"
+macro_rules! m {
+    ($($t:ident),*) => {
+        ${count(t)}
+    }
+}
+
+fn test() {
+    0;
+    1;
+    2;
+}
+"#]],
+    );
+}
+
+#[test]
+fn count_with_depth() {
+    check(
+        r#"
+macro_rules! foo {
+    ($( $( $($t:ident)* ),* );*) => {
+        $(
+            {
+                let depth_none = ${count(t)};
+                let depth_zero = ${count(t, 0)};
+                let depth_one = ${count(t, 1)};
+            }
+        )*
+    }
+}
+
+fn bar() {
+    foo!(
+        a a a, a, a a;
+        a a a
+    )
+}
+"#,
+        expect![[r#"
+macro_rules! foo {
+    ($( $( $($t:ident)* ),* );*) => {
+        $(
+            {
+                let depth_none = ${count(t)};
+                let depth_zero = ${count(t, 0)};
+                let depth_one = ${count(t, 1)};
+            }
+        )*
+    }
+}
+
+fn bar() {
+     {
+        let depth_none = 6;
+        let depth_zero = 3;
+        let depth_one = 6;
+    } {
+        let depth_none = 3;
+        let depth_zero = 1;
+        let depth_one = 3;
+    }
+}
+"#]],
+    );
+}
+
+#[test]
+fn count_depth_out_of_bounds() {
+    check(
+        r#"
+macro_rules! foo {
+    ($($t:ident)*) => { ${count(t, 1)} };
+    ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+    ($($t:ident)*) => { ${count(t, 1024)} };
+    ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+    foo!(a b);
+    foo!(1 2; 3);
+    bar!(a b);
+    bar!(1 2; 3);
+}
+"#,
+        expect![[r#"
+macro_rules! foo {
+    ($($t:ident)*) => { ${count(t, 1)} };
+    ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+    ($($t:ident)*) => { ${count(t, 1024)} };
+    ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+    /* error: ${count} out of bounds */;
+    /* error: ${count} out of bounds */;
+    /* error: ${count} out of bounds */;
+    /* error: ${count} out of bounds */;
+}
+"#]],
+    );
+}
+
+#[test]
+fn misplaced_count() {
+    check(
+        r#"
+macro_rules! foo {
+    ($($t:ident)*) => { $(${count(t)})* };
+    ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+    foo!(a b c);
+    foo!(1);
+}
+"#,
+        expect![[r#"
+macro_rules! foo {
+    ($($t:ident)*) => { $(${count(t)})* };
+    ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+    /* error: ${count} misplaced */;
+    /* error: ${count} misplaced */;
+}
+"#]],
+    );
+}
+
+#[test]
+fn malformed_count() {
+    check(
+        r#"
+macro_rules! too_many_args {
+    ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+    ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+    ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+    too_many_args!();
+    depth_suffixed!();
+    depth_too_large!();
+}
+"#,
+        expect![[r#"
+macro_rules! too_many_args {
+    ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+    ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+    ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+    /* error: invalid macro definition: invalid metavariable expression */;
+    /* error: invalid macro definition: invalid metavariable expression */;
+    /* error: invalid macro definition: invalid metavariable expression */;
+}
+"#]],
+    );
+}
+
+#[test]
+fn count_interaction_with_empty_binding() {
+    // FIXME: Should this error? rustc currently accepts it.
+    check(
+        r#"
+macro_rules! m {
+    ($($t:ident),*) => {
+        ${count(t, 100)}
+    }
+}
+
+fn test() {
+    m!();
+}
+"#,
+        expect![[r#"
+macro_rules! m {
+    ($($t:ident),*) => {
+        ${count(t, 100)}
+    }
+}
+
+fn test() {
+    0;
+}
+"#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index b663a291789..d8e4a4dcc7c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst  {
 
 #[derive(Clone)] struct CharEscapeDebugContinue;
 impl Fn<(char, )> for CharEscapeDebugContinue {
-    #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
+    #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
             c.escape_debug_ext(false )
         }
     }
 }
 impl FnMut<(char, )> for CharEscapeDebugContinue {
-    #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
+    #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
         Fn::call(&*self , (c, ))
     }
 }
 impl FnOnce<(char, )> for CharEscapeDebugContinue {
     type Output = char::EscapeDebug;
-    #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
+    #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
         Fn::call(&self , (c, ))
     }
 }
 #[derive(Clone)] struct CharEscapeUnicode;
 impl Fn<(char, )> for CharEscapeUnicode {
-    #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
+    #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
             c.escape_unicode()
         }
     }
 }
 impl FnMut<(char, )> for CharEscapeUnicode {
-    #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
+    #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
         Fn::call(&*self , (c, ))
     }
 }
 impl FnOnce<(char, )> for CharEscapeUnicode {
     type Output = char::EscapeUnicode;
-    #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
+    #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
         Fn::call(&self , (c, ))
     }
 }
 #[derive(Clone)] struct CharEscapeDefault;
 impl Fn<(char, )> for CharEscapeDefault {
-    #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
+    #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
             c.escape_default()
         }
     }
 }
 impl FnMut<(char, )> for CharEscapeDefault {
-    #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
+    #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
         Fn::call(&*self , (c, ))
     }
 }
 impl FnOnce<(char, )> for CharEscapeDefault {
     type Output = char::EscapeDefault;
-    #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
+    #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
         Fn::call(&self , (c, ))
     }
 }
@@ -833,7 +833,7 @@ macro_rules! rgb_color {
 /* parse error: expected SEMICOLON */
 /* parse error: expected expression, item or let statement */
 pub fn new() {
-    let _ = 0as u32<<(8+8);
+    let _ = 0 as u32<<(8+8);
 }
 // MACRO_ITEMS@0..31
 //   FN@0..31
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index b8d2ca687c9..ae56934f632 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
 macro_rules! m2 { ($x:ident) => {} }
 
 /* error: invalid macro definition: expected subtree */
-/* error: Failed to lower macro args to token tree */
+/* error: invalid token tree */
 "#]],
     )
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 314bf22b95e..4a62696df08 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -14,7 +14,7 @@ mod builtin_fn_macro;
 mod builtin_derive_macro;
 mod proc_macros;
 
-use std::{iter, ops::Range, sync::Arc};
+use std::{iter, ops::Range, sync};
 
 use ::mbe::TokenMap;
 use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
@@ -33,8 +33,13 @@ use syntax::{
 use tt::token_id::{Subtree, TokenId};
 
 use crate::{
-    db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver,
-    src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId,
+    db::DefDatabase,
+    macro_id_to_def_id,
+    nameres::{DefMap, MacroSubNs, ModuleSource},
+    resolver::HasResolver,
+    src::HasSource,
+    test_db::TestDB,
+    AdtId, AsMacroCall, Lookup, ModuleDefId,
 };
 
 #[track_caller]
@@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
         ProcMacro {
             name: "identity_when_valid".into(),
             kind: base_db::ProcMacroKind::Attr,
-            expander: Arc::new(IdentityWhenValidProcMacroExpander),
+            expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
         },
     )];
     let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
     let krate = db.crate_graph().iter().next().unwrap();
     let def_map = db.crate_def_map(krate);
-    let local_id = def_map.root();
+    let local_id = DefMap::ROOT;
     let module = def_map.module_id(local_id);
     let resolver = module.resolver(&db);
     let source = def_map[local_id].definition_source(&db);
@@ -125,21 +130,17 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
 
     for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
         let macro_call = InFile::new(source.file_id, &macro_call);
-        let mut error = None;
-        let macro_call_id = macro_call
-            .as_call_id_with_errors(
-                &db,
-                krate,
-                |path| {
-                    resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
-                },
-                &mut |err| error = Some(err),
-            )
-            .unwrap()
+        let res = macro_call
+            .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))
+            })
             .unwrap();
+        let macro_call_id = res.value.unwrap();
         let macro_file = MacroFile { macro_call_id };
         let mut expansion_result = db.parse_macro_expansion(macro_file);
-        expansion_result.err = expansion_result.err.or(error);
+        expansion_result.err = expansion_result.err.or(res.err);
         expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id)));
     }
 
@@ -157,34 +158,33 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
         if let Some(err) = exp.err {
             format_to!(expn_text, "/* error: {} */", err);
         }
-        if let Some((parse, token_map)) = exp.value {
-            if expect_errors {
-                assert!(!parse.errors().is_empty(), "no parse errors in expansion");
-                for e in parse.errors() {
-                    format_to!(expn_text, "/* parse error: {} */\n", e);
-                }
-            } else {
-                assert!(
-                    parse.errors().is_empty(),
-                    "parse errors in expansion: \n{:#?}",
-                    parse.errors()
-                );
+        let (parse, token_map) = exp.value;
+        if expect_errors {
+            assert!(!parse.errors().is_empty(), "no parse errors in expansion");
+            for e in parse.errors() {
+                format_to!(expn_text, "/* parse error: {} */\n", e);
             }
-            let pp = pretty_print_macro_expansion(
-                parse.syntax_node(),
-                show_token_ids.then_some(&*token_map),
+        } else {
+            assert!(
+                parse.errors().is_empty(),
+                "parse errors in expansion: \n{:#?}",
+                parse.errors()
             );
-            let indent = IndentLevel::from_node(call.syntax());
-            let pp = reindent(indent, pp);
-            format_to!(expn_text, "{}", pp);
+        }
+        let pp = pretty_print_macro_expansion(
+            parse.syntax_node(),
+            show_token_ids.then_some(&*token_map),
+        );
+        let indent = IndentLevel::from_node(call.syntax());
+        let pp = reindent(indent, pp);
+        format_to!(expn_text, "{}", pp);
 
-            if tree {
-                let tree = format!("{:#?}", parse.syntax_node())
-                    .split_inclusive('\n')
-                    .map(|line| format!("// {line}"))
-                    .collect::<String>();
-                format_to!(expn_text, "\n{}", tree)
-            }
+        if tree {
+            let tree = format!("{:#?}", parse.syntax_node())
+                .split_inclusive('\n')
+                .map(|line| format!("// {line}"))
+                .collect::<String>();
+            format_to!(expn_text, "\n{}", tree)
         }
         let range = call.syntax().text_range();
         let range: Range<usize> = range.into();
@@ -287,6 +287,7 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str
         let curr_kind = token.kind();
         let space = match (prev_kind, curr_kind) {
             _ if prev_kind.is_trivia() || curr_kind.is_trivia() => "",
+            _ if prev_kind.is_literal() && !curr_kind.is_punct() => " ",
             (T!['{'], T!['}']) => "",
             (T![=], _) | (_, T![=]) => " ",
             (_, T!['{']) => " ",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 4efe8c58a69..9b520bc3030 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -57,9 +57,9 @@ mod path_resolution;
 #[cfg(test)]
 mod tests;
 
-use std::{cmp::Ord, ops::Deref, sync::Arc};
+use std::{cmp::Ord, ops::Deref};
 
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Edition, FileId, ProcMacroKind};
 use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
 use itertools::Itertools;
 use la_arena::Arena;
@@ -67,6 +67,7 @@ use profile::Count;
 use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::format_to;
 use syntax::{ast, SmolStr};
+use triomphe::Arc;
 
 use crate::{
     db::DefDatabase,
@@ -76,7 +77,8 @@ use crate::{
     path::ModPath,
     per_ns::PerNs,
     visibility::Visibility,
-    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
+    AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId,
+    ProcMacroId,
 };
 
 /// Contains the results of (early) name resolution.
@@ -92,7 +94,6 @@ use crate::{
 pub struct DefMap {
     _c: Count<Self>,
     block: Option<BlockInfo>,
-    root: LocalModuleId,
     modules: Arena<ModuleData>,
     krate: CrateId,
     /// The prelude module for this crate. This either comes from an import
@@ -102,7 +103,22 @@ pub struct DefMap {
     /// 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>,
-    /// The extern prelude is only populated for non-block DefMaps
+    /// `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>,
+
+    /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
+    /// attributes.
+    derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
+
+    diagnostics: Vec<DefDiagnostic>,
+
+    data: Arc<DefMapCrateData>,
+}
+
+/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct DefMapCrateData {
     extern_prelude: FxHashMap<Name, ModuleId>,
 
     /// Side table for resolving derive helpers.
@@ -110,9 +126,6 @@ pub struct DefMap {
     fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
     /// The error that occurred when failing to load the proc-macro dll.
     proc_macro_loading_error: Option<Box<str>>,
-    /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
-    /// attributes.
-    derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
 
     /// Custom attributes registered with `#![register_attr]`.
     registered_attrs: Vec<SmolStr>,
@@ -122,10 +135,36 @@ pub struct DefMap {
     unstable_features: FxHashSet<SmolStr>,
     /// #[rustc_coherence_is_core]
     rustc_coherence_is_core: bool,
+    no_core: bool,
+    no_std: bool,
 
     edition: Edition,
     recursion_limit: Option<u32>,
-    diagnostics: Vec<DefDiagnostic>,
+}
+
+impl DefMapCrateData {
+    fn shrink_to_fit(&mut self) {
+        let Self {
+            extern_prelude,
+            exported_derives,
+            fn_proc_macro_mapping,
+            registered_attrs,
+            registered_tools,
+            unstable_features,
+            proc_macro_loading_error: _,
+            rustc_coherence_is_core: _,
+            no_core: _,
+            no_std: _,
+            edition: _,
+            recursion_limit: _,
+        } = self;
+        extern_prelude.shrink_to_fit();
+        exported_derives.shrink_to_fit();
+        fn_proc_macro_mapping.shrink_to_fit();
+        registered_attrs.shrink_to_fit();
+        registered_tools.shrink_to_fit();
+        unstable_features.shrink_to_fit();
+    }
 }
 
 /// For `DefMap`s computed for a block expression, this stores its location in the parent map.
@@ -134,7 +173,23 @@ struct BlockInfo {
     /// The `BlockId` this `DefMap` was created from.
     block: BlockId,
     /// The containing module.
-    parent: ModuleId,
+    parent: BlockRelativeModuleId,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+struct BlockRelativeModuleId {
+    block: Option<BlockId>,
+    local_id: LocalModuleId,
+}
+
+impl BlockRelativeModuleId {
+    fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
+        self.into_module(krate).def_map(db)
+    }
+
+    fn into_module(self, krate: CrateId) -> ModuleId {
+        ModuleId { krate, block: self.block, local_id: self.local_id }
+    }
 }
 
 impl std::ops::Index<LocalModuleId> for DefMap {
@@ -224,6 +279,8 @@ pub struct ModuleData {
 }
 
 impl DefMap {
+    pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
+
     pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
         let _p = profile::span("crate_def_map_query").detail(|| {
             db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
@@ -243,17 +300,10 @@ impl DefMap {
         Arc::new(def_map)
     }
 
-    pub(crate) fn block_def_map_query(
-        db: &dyn DefDatabase,
-        block_id: BlockId,
-    ) -> Option<Arc<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 item_tree = tree_id.item_tree(db);
-        if item_tree.top_level_items().is_empty() {
-            return None;
-        }
 
         let parent_map = block.module.def_map(db);
         let krate = block.module.krate;
@@ -265,36 +315,48 @@ impl DefMap {
         let module_data =
             ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
 
-        let mut def_map = DefMap::empty(krate, parent_map.edition, module_data);
-        def_map.block = Some(BlockInfo { block: block_id, parent: block.module });
+        let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
+        def_map.data = parent_map.data.clone();
+        def_map.block = Some(BlockInfo {
+            block: block_id,
+            parent: BlockRelativeModuleId {
+                block: block.module.block,
+                local_id: block.module.local_id,
+            },
+        });
 
         let def_map = collector::collect_defs(db, def_map, tree_id);
-        Some(Arc::new(def_map))
+        Arc::new(def_map)
     }
 
     fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
         let mut modules: Arena<ModuleData> = Arena::default();
         let root = modules.alloc(module_data);
+        assert_eq!(root, Self::ROOT);
 
         DefMap {
             _c: Count::new(),
             block: None,
+            modules,
             krate,
-            edition,
-            recursion_limit: None,
-            extern_prelude: FxHashMap::default(),
-            exported_derives: FxHashMap::default(),
-            fn_proc_macro_mapping: FxHashMap::default(),
-            proc_macro_loading_error: None,
-            derive_helpers_in_scope: FxHashMap::default(),
             prelude: None,
-            root,
-            modules,
-            registered_attrs: Vec::new(),
-            registered_tools: Vec::new(),
-            unstable_features: FxHashSet::default(),
+            macro_use_prelude: FxHashMap::default(),
+            derive_helpers_in_scope: FxHashMap::default(),
             diagnostics: Vec::new(),
-            rustc_coherence_is_core: false,
+            data: Arc::new(DefMapCrateData {
+                extern_prelude: FxHashMap::default(),
+                exported_derives: FxHashMap::default(),
+                fn_proc_macro_mapping: FxHashMap::default(),
+                proc_macro_loading_error: None,
+                registered_attrs: Vec::new(),
+                registered_tools: Vec::new(),
+                unstable_features: FxHashSet::default(),
+                rustc_coherence_is_core: false,
+                no_core: false,
+                no_std: false,
+                edition,
+                recursion_limit: None,
+            }),
         }
     }
 
@@ -317,31 +379,31 @@ impl DefMap {
     }
 
     pub fn registered_tools(&self) -> &[SmolStr] {
-        &self.registered_tools
+        &self.data.registered_tools
     }
 
     pub fn registered_attrs(&self) -> &[SmolStr] {
-        &self.registered_attrs
+        &self.data.registered_attrs
     }
 
     pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
-        self.unstable_features.contains(feature)
+        self.data.unstable_features.contains(feature)
     }
 
     pub fn is_rustc_coherence_is_core(&self) -> bool {
-        self.rustc_coherence_is_core
+        self.data.rustc_coherence_is_core
     }
 
-    pub fn root(&self) -> LocalModuleId {
-        self.root
+    pub fn is_no_std(&self) -> bool {
+        self.data.no_std || self.data.no_core
     }
 
     pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
-        self.fn_proc_macro_mapping.get(&id).copied()
+        self.data.fn_proc_macro_mapping.get(&id).copied()
     }
 
     pub fn proc_macro_loading_error(&self) -> Option<&str> {
-        self.proc_macro_loading_error.as_deref()
+        self.data.proc_macro_loading_error.as_deref()
     }
 
     pub fn krate(&self) -> CrateId {
@@ -356,8 +418,12 @@ impl DefMap {
         self.prelude
     }
 
-    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ {
-        self.extern_prelude.iter()
+    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
+        self.data.extern_prelude.iter().map(|(name, def)| (name, *def))
+    }
+
+    pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+        self.macro_use_prelude.iter().map(|(name, def)| (name, *def))
     }
 
     pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
@@ -365,11 +431,8 @@ impl DefMap {
         ModuleId { krate: self.krate, local_id, block }
     }
 
-    pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId {
-        self.with_ancestor_maps(db, self.root, &mut |def_map, _module| {
-            if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None }
-        })
-        .expect("DefMap chain without root")
+    pub(crate) fn crate_root(&self) -> ModuleId {
+        ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT }
     }
 
     pub(crate) fn resolve_path(
@@ -378,9 +441,16 @@ impl DefMap {
         original_module: LocalModuleId,
         path: &ModPath,
         shadow: BuiltinShadowMode,
+        expected_macro_subns: Option<MacroSubNs>,
     ) -> (PerNs, Option<usize>) {
-        let res =
-            self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
+        let res = self.resolve_path_fp_with_macro(
+            db,
+            ResolveMode::Other,
+            original_module,
+            path,
+            shadow,
+            expected_macro_subns,
+        );
         (res.resolved_def, res.segment_index)
     }
 
@@ -397,6 +467,7 @@ impl DefMap {
             original_module,
             path,
             shadow,
+            None, // Currently this function isn't used for macro resolution.
         );
         (res.resolved_def, res.segment_index)
     }
@@ -416,7 +487,7 @@ impl DefMap {
         }
         let mut block = self.block;
         while let Some(block_info) = block {
-            let parent = block_info.parent.def_map(db);
+            let parent = block_info.parent.def_map(db, self.krate);
             if let Some(it) = f(&parent, block_info.parent.local_id) {
                 return Some(it);
             }
@@ -429,7 +500,8 @@ impl DefMap {
     /// If this `DefMap` is for a block expression, returns the module containing the block (which
     /// might again be a block, or a module inside a block).
     pub fn parent(&self) -> Option<ModuleId> {
-        Some(self.block?.parent)
+        let BlockRelativeModuleId { block, local_id } = self.block?.parent;
+        Some(ModuleId { krate: self.krate, block, local_id })
     }
 
     /// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing
@@ -437,7 +509,13 @@ impl DefMap {
     pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
         match self[local_mod].parent {
             Some(parent) => Some(self.module_id(parent)),
-            None => self.block.map(|block| block.parent),
+            None => {
+                self.block.map(
+                    |BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| {
+                        ModuleId { krate: self.krate, block, local_id }
+                    },
+                )
+            }
         }
     }
 
@@ -448,25 +526,31 @@ impl DefMap {
         let mut arc;
         let mut current_map = self;
         while let Some(block) = current_map.block {
-            go(&mut buf, current_map, "block scope", current_map.root);
+            go(&mut buf, db, current_map, "block scope", Self::ROOT);
             buf.push('\n');
-            arc = block.parent.def_map(db);
+            arc = block.parent.def_map(db, self.krate);
             current_map = &arc;
         }
-        go(&mut buf, current_map, "crate", current_map.root);
+        go(&mut buf, db, current_map, "crate", Self::ROOT);
         return buf;
 
-        fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
+        fn go(
+            buf: &mut String,
+            db: &dyn DefDatabase,
+            map: &DefMap,
+            path: &str,
+            module: LocalModuleId,
+        ) {
             format_to!(buf, "{}\n", path);
 
-            map.modules[module].scope.dump(buf);
+            map.modules[module].scope.dump(db.upcast(), buf);
 
             for (name, child) in
                 map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
             {
-                let path = format!("{path}::{name}");
+                let path = format!("{path}::{}", name.display(db.upcast()));
                 buf.push('\n');
-                go(buf, map, &path, *child);
+                go(buf, db, map, &path, *child);
             }
         }
     }
@@ -477,7 +561,7 @@ impl DefMap {
         let mut current_map = self;
         while let Some(block) = current_map.block {
             format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
-            arc = block.parent.def_map(db);
+            arc = block.parent.def_map(db, self.krate);
             current_map = &arc;
         }
 
@@ -489,34 +573,20 @@ impl DefMap {
         // Exhaustive match to require handling new fields.
         let Self {
             _c: _,
-            exported_derives,
-            extern_prelude,
+            macro_use_prelude,
             diagnostics,
             modules,
-            registered_attrs,
-            registered_tools,
-            fn_proc_macro_mapping,
             derive_helpers_in_scope,
-            unstable_features,
-            proc_macro_loading_error: _,
             block: _,
-            edition: _,
-            recursion_limit: _,
             krate: _,
             prelude: _,
-            root: _,
-            rustc_coherence_is_core: _,
+            data: _,
         } = self;
 
-        extern_prelude.shrink_to_fit();
-        exported_derives.shrink_to_fit();
+        macro_use_prelude.shrink_to_fit();
         diagnostics.shrink_to_fit();
         modules.shrink_to_fit();
-        registered_attrs.shrink_to_fit();
-        registered_tools.shrink_to_fit();
-        fn_proc_macro_mapping.shrink_to_fit();
         derive_helpers_in_scope.shrink_to_fit();
-        unstable_features.shrink_to_fit();
         for (_, module) in modules.iter_mut() {
             module.children.shrink_to_fit();
             module.scope.shrink_to_fit();
@@ -529,7 +599,7 @@ impl DefMap {
     }
 
     pub fn recursion_limit(&self) -> Option<u32> {
-        self.recursion_limit
+        self.data.recursion_limit
     }
 }
 
@@ -564,3 +634,48 @@ pub enum ModuleSource {
     Module(ast::Module),
     BlockExpr(ast::BlockExpr),
 }
+
+/// See `sub_namespace_match()`.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum MacroSubNs {
+    /// Function-like macros, suffixed with `!`.
+    Bang,
+    /// Macros inside attributes, i.e. attribute macros and derive macros.
+    Attr,
+}
+
+impl MacroSubNs {
+    fn from_id(db: &dyn DefDatabase, macro_id: MacroId) -> Self {
+        let expander = match macro_id {
+            MacroId::Macro2Id(it) => it.lookup(db).expander,
+            MacroId::MacroRulesId(it) => it.lookup(db).expander,
+            MacroId::ProcMacroId(it) => {
+                return match it.lookup(db).kind {
+                    ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
+                    ProcMacroKind::FuncLike => Self::Bang,
+                };
+            }
+        };
+
+        // Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently.
+        match expander {
+            MacroExpander::Declarative
+            | MacroExpander::BuiltIn(_)
+            | MacroExpander::BuiltInEager(_) => Self::Bang,
+            MacroExpander::BuiltInAttr(_) | MacroExpander::BuiltInDerive(_) => Self::Attr,
+        }
+    }
+}
+
+/// Quoted from [rustc]:
+/// Macro namespace is separated into two sub-namespaces, one for bang macros and
+/// one for attribute-like macros (attributes, derives).
+/// We ignore resolutions from one sub-namespace when searching names in scope for another.
+///
+/// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75
+fn sub_namespace_match(candidate: Option<MacroSubNs>, expected: Option<MacroSubNs>) -> bool {
+    match (candidate, expected) {
+        (Some(candidate), Some(expected)) => candidate == expected,
+        _ => true,
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
index 79cabeb0fb7..a7abf445918 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
@@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
 use syntax::{ast, SmolStr};
 
 use crate::{
-    attr_macro_as_call_id, builtin_attr,
+    attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
+    attr_macro_as_call_id,
     db::DefDatabase,
     item_scope::BuiltinShadowMode,
     macro_id_to_def_id,
@@ -13,7 +14,7 @@ use crate::{
     AstIdWithPath, LocalModuleId, UnresolvedMacro,
 };
 
-use super::DefMap;
+use super::{DefMap, MacroSubNs};
 
 pub enum ResolvedAttr {
     /// Attribute resolved to an attribute macro.
@@ -42,9 +43,12 @@ impl DefMap {
             original_module,
             &ast_id.path,
             BuiltinShadowMode::Module,
+            Some(MacroSubNs::Attr),
         );
         let def = match resolved_res.resolved_def.take_macros() {
             Some(def) => {
+                // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
+                // macro, or even function-like macro when the path is qualified.
                 if def.is_attribute(db) {
                     def
                 } else {
@@ -60,7 +64,6 @@ impl DefMap {
             attr,
             self.krate,
             macro_id_to_def_id(db, def),
-            false,
         )))
     }
 
@@ -75,20 +78,16 @@ impl DefMap {
             let name = name.to_smol_str();
             let pred = |n: &_| *n == name;
 
-            let registered = self.registered_tools.iter().map(SmolStr::as_str);
-            let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+            let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
+            let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
             // FIXME: tool modules can be shadowed by actual modules
             if is_tool {
                 return true;
             }
 
             if segments.len() == 1 {
-                let registered = self.registered_attrs.iter().map(SmolStr::as_str);
-                let is_inert = builtin_attr::INERT_ATTRIBUTES
-                    .iter()
-                    .map(|it| it.name)
-                    .chain(registered)
-                    .any(pred);
+                let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
+                let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
                 return is_inert;
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index ddcee77ec4c..06542b4b1e9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -5,7 +5,7 @@
 
 use std::{iter, mem};
 
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Dependency, Edition, FileId};
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
@@ -14,10 +14,11 @@ use hir_expand::{
     builtin_attr_macro::find_builtin_attr,
     builtin_derive_macro::find_builtin_derive,
     builtin_fn_macro::find_builtin_macro,
+    hygiene::Hygiene,
     name::{name, AsName, Name},
     proc_macro::ProcMacroExpander,
-    ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
-    MacroDefKind,
+    ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
+    MacroDefId, MacroDefKind,
 };
 use itertools::{izip, Itertools};
 use la_arena::Idx;
@@ -25,6 +26,7 @@ use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::always;
 use syntax::{ast, SmolStr};
+use triomphe::Arc;
 
 use crate::{
     attr::Attrs,
@@ -33,8 +35,8 @@ use crate::{
     derive_macro_as_call_id,
     item_scope::{ImportType, PerNsGlobImports},
     item_tree::{
-        self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
-        MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
+        self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
+        MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
     },
     macro_call_as_call_id, macro_id_to_def_id,
     nameres::{
@@ -42,7 +44,8 @@ use crate::{
         mod_resolution::ModDir,
         path_resolution::ReachedFixedPoint,
         proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
-        BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
+        sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
+        ResolveMode,
     },
     path::{ImportAlias, ModPath, PathKind},
     per_ns::PerNs,
@@ -59,7 +62,7 @@ static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
 static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
 static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
 
-pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap {
+pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
     let crate_graph = db.crate_graph();
 
     let mut deps = FxHashMap::default();
@@ -67,36 +70,33 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
     let krate = &crate_graph[def_map.krate];
     for dep in &krate.dependencies {
         tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
-        let dep_def_map = db.crate_def_map(dep.crate_id);
-        let dep_root = dep_def_map.module_id(dep_def_map.root);
 
-        deps.insert(dep.as_name(), dep_root);
-
-        if dep.is_prelude() && !tree_id.is_block() {
-            def_map.extern_prelude.insert(dep.as_name(), dep_root);
-        }
+        deps.insert(dep.as_name(), dep.clone());
     }
 
     let cfg_options = &krate.cfg_options;
-    let proc_macros = match &krate.proc_macro {
-        Ok(proc_macros) => {
-            proc_macros
-                .iter()
-                .enumerate()
-                .map(|(idx, it)| {
-                    // FIXME: a hacky way to create a Name from string.
-                    let name =
-                        tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
-                    (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
-                })
-                .collect()
-        }
-        Err(e) => {
-            def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str());
-            Vec::new()
+
+    let is_proc_macro = krate.is_proc_macro;
+    let proc_macros = if is_proc_macro {
+        match db.proc_macros().get(&def_map.krate) {
+            Some(Ok(proc_macros)) => {
+                Ok(proc_macros
+                    .iter()
+                    .enumerate()
+                    .map(|(idx, it)| {
+                        // FIXME: a hacky way to create a Name from string.
+                        let name =
+                            tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
+                        (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
+                    })
+                    .collect())
+            }
+            Some(Err(e)) => Err(e.clone().into_boxed_str()),
+            None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
         }
+    } else {
+        Ok(vec![])
     };
-    let is_proc_macro = krate.is_proc_macro;
 
     let mut collector = DefCollector {
         db,
@@ -112,6 +112,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
         from_glob_import: Default::default(),
         skip_attrs: Default::default(),
         is_proc_macro,
+        hygienes: FxHashMap::default(),
     };
     if tree_id.is_block() {
         collector.seed_with_inner(tree_id);
@@ -238,7 +239,7 @@ enum MacroDirectiveKind {
 struct DefCollector<'a> {
     db: &'a dyn DefDatabase,
     def_map: DefMap,
-    deps: FxHashMap<Name, ModuleId>,
+    deps: FxHashMap<Name, Dependency>,
     glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
     unresolved_imports: Vec<ImportDirective>,
     indeterminate_imports: Vec<ImportDirective>,
@@ -249,7 +250,7 @@ struct DefCollector<'a> {
     /// built by the build system, and is the list of proc. macros we can actually expand. It is
     /// empty when proc. macro support is disabled (in which case we still do name resolution for
     /// them).
-    proc_macros: Vec<(Name, ProcMacroExpander)>,
+    proc_macros: Result<Vec<(Name, ProcMacroExpander)>, Box<str>>,
     is_proc_macro: bool,
     from_glob_import: PerNsGlobImports,
     /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
@@ -259,6 +260,12 @@ struct DefCollector<'a> {
     /// This also stores the attributes to skip when we resolve derive helpers and non-macro
     /// non-builtin attributes in general.
     skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
+    /// `Hygiene` cache, because `Hygiene` construction is expensive.
+    ///
+    /// Almost all paths should have been lowered to `ModPath` during `ItemTree` construction.
+    /// However, `DefCollector` still needs to lower paths in attributes, in particular those in
+    /// derive meta item list.
+    hygienes: FxHashMap<HirFileId, Hygiene>,
 }
 
 impl DefCollector<'_> {
@@ -267,86 +274,117 @@ impl DefCollector<'_> {
 
         let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
         let item_tree = self.db.file_item_tree(file_id.into());
-        let module_id = self.def_map.root;
+        let module_id = DefMap::ROOT;
 
         let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
-        if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
-            self.inject_prelude(&attrs);
-
-            // Process other crate-level attributes.
-            for attr in &*attrs {
-                let attr_name = match attr.path.as_ident() {
-                    Some(name) => name,
-                    None => continue,
-                };
+        let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
 
-                if *attr_name == hir_expand::name![recursion_limit] {
-                    if let Some(limit) = attr.string_value() {
-                        if let Ok(limit) = limit.parse() {
-                            self.def_map.recursion_limit = Some(limit);
-                        }
-                    }
-                    continue;
+        if let Err(e) = &self.proc_macros {
+            crate_data.proc_macro_loading_error = Some(e.clone());
+        }
+
+        for (name, dep) in &self.deps {
+            if dep.is_prelude() {
+                crate_data.extern_prelude.insert(
+                    name.clone(),
+                    ModuleId { krate: dep.crate_id, block: None, local_id: DefMap::ROOT },
+                );
+            }
+        }
+
+        // Process other crate-level attributes.
+        for attr in &*attrs {
+            if let Some(cfg) = attr.cfg() {
+                if self.cfg_options.check(&cfg) == Some(false) {
+                    return;
                 }
+            }
+            let attr_name = match attr.path.as_ident() {
+                Some(name) => name,
+                None => continue,
+            };
 
-                if *attr_name == hir_expand::name![crate_type] {
-                    if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
-                        self.is_proc_macro = true;
+            if *attr_name == hir_expand::name![recursion_limit] {
+                if let Some(limit) = attr.string_value() {
+                    if let Ok(limit) = limit.parse() {
+                        crate_data.recursion_limit = Some(limit);
                     }
-                    continue;
                 }
+                continue;
+            }
 
-                if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
-                    self.def_map.rustc_coherence_is_core = true;
-                    continue;
+            if *attr_name == hir_expand::name![crate_type] {
+                if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
+                    self.is_proc_macro = true;
                 }
+                continue;
+            }
 
-                if *attr_name == hir_expand::name![feature] {
-                    let features =
-                        attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
-                            |feat| match feat.segments() {
-                                [name] => Some(name.to_smol_str()),
-                                _ => None,
-                            },
-                        );
-                    self.def_map.unstable_features.extend(features);
-                }
+            if *attr_name == hir_expand::name![no_core] {
+                crate_data.no_core = true;
+                continue;
+            }
 
-                let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
-                    || *attr_name == hir_expand::name![register_tool];
-                if !attr_is_register_like {
-                    continue;
-                }
+            if *attr_name == hir_expand::name![no_std] {
+                crate_data.no_std = true;
+                continue;
+            }
 
-                let registered_name = match attr.single_ident_value() {
-                    Some(ident) => ident.as_name(),
-                    _ => continue,
-                };
+            if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
+                crate_data.rustc_coherence_is_core = true;
+                continue;
+            }
 
-                if *attr_name == hir_expand::name![register_attr] {
-                    self.def_map.registered_attrs.push(registered_name.to_smol_str());
-                    cov_mark::hit!(register_attr);
-                } else {
-                    self.def_map.registered_tools.push(registered_name.to_smol_str());
-                    cov_mark::hit!(register_tool);
-                }
+            if *attr_name == hir_expand::name![feature] {
+                let hygiene = &Hygiene::new_unhygienic();
+                let features = attr
+                    .parse_path_comma_token_tree(self.db.upcast(), hygiene)
+                    .into_iter()
+                    .flatten()
+                    .filter_map(|feat| match feat.segments() {
+                        [name] => Some(name.to_smol_str()),
+                        _ => None,
+                    });
+                crate_data.unstable_features.extend(features);
             }
 
-            ModCollector {
-                def_collector: self,
-                macro_depth: 0,
-                module_id,
-                tree_id: TreeId::new(file_id.into(), None),
-                item_tree: &item_tree,
-                mod_dir: ModDir::root(),
+            let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
+                || *attr_name == hir_expand::name![register_tool];
+            if !attr_is_register_like {
+                continue;
+            }
+
+            let registered_name = match attr.single_ident_value() {
+                Some(ident) => ident.as_name(),
+                _ => continue,
+            };
+
+            if *attr_name == hir_expand::name![register_attr] {
+                crate_data.registered_attrs.push(registered_name.to_smol_str());
+                cov_mark::hit!(register_attr);
+            } else {
+                crate_data.registered_tools.push(registered_name.to_smol_str());
+                cov_mark::hit!(register_tool);
             }
-            .collect_in_top_module(item_tree.top_level_items());
         }
+
+        crate_data.shrink_to_fit();
+        self.inject_prelude();
+
+        ModCollector {
+            def_collector: self,
+            macro_depth: 0,
+            module_id,
+            tree_id: TreeId::new(file_id.into(), None),
+            item_tree: &item_tree,
+            mod_dir: ModDir::root(),
+        }
+        .collect_in_top_module(item_tree.top_level_items());
     }
 
     fn seed_with_inner(&mut self, tree_id: TreeId) {
         let item_tree = tree_id.item_tree(self.db);
-        let module_id = self.def_map.root;
+        let module_id = DefMap::ROOT;
 
         let is_cfg_enabled = item_tree
             .top_level_attrs(self.db, self.def_map.krate)
@@ -428,7 +466,7 @@ impl DefCollector<'_> {
             // Additionally, while the proc macro entry points must be `pub`, they are not publicly
             // exported in type/value namespace. This function reduces the visibility of all items
             // in the crate root that aren't proc macros.
-            let root = self.def_map.root;
+            let root = DefMap::ROOT;
             let module_id = self.def_map.module_id(root);
             let root = &mut self.def_map.modules[root];
             root.scope.censor_non_proc_macros(module_id);
@@ -456,12 +494,8 @@ impl DefCollector<'_> {
                         directive.module_id,
                         MacroCallKind::Attr {
                             ast_id: ast_id.ast_id,
-                            attr_args: std::sync::Arc::new((
-                                tt::Subtree::empty(),
-                                Default::default(),
-                            )),
+                            attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
                             invoc_attr_index: attr.id,
-                            is_derive: false,
                         },
                         attr.path().clone(),
                     ));
@@ -495,15 +529,15 @@ impl DefCollector<'_> {
         }
     }
 
-    fn inject_prelude(&mut self, crate_attrs: &Attrs) {
+    fn inject_prelude(&mut self) {
         // See compiler/rustc_builtin_macros/src/standard_library_imports.rs
 
-        if crate_attrs.by_key("no_core").exists() {
+        if self.def_map.data.no_core {
             // libcore does not get a prelude.
             return;
         }
 
-        let krate = if crate_attrs.by_key("no_std").exists() {
+        let krate = if self.def_map.data.no_std {
             name![core]
         } else {
             let std = name![std];
@@ -516,43 +550,31 @@ impl DefCollector<'_> {
             }
         };
 
-        let edition = match self.def_map.edition {
+        let edition = match self.def_map.data.edition {
             Edition::Edition2015 => name![rust_2015],
             Edition::Edition2018 => name![rust_2018],
             Edition::Edition2021 => name![rust_2021],
         };
 
-        let path_kind = match self.def_map.edition {
+        let path_kind = match self.def_map.data.edition {
             Edition::Edition2015 => PathKind::Plain,
             _ => PathKind::Abs,
         };
-        let path =
-            ModPath::from_segments(path_kind, [krate.clone(), name![prelude], edition].into_iter());
-        // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
-        // FIXME remove this fallback
-        let fallback_path =
-            ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].into_iter());
-
-        for path in &[path, fallback_path] {
-            let (per_ns, _) = self.def_map.resolve_path(
-                self.db,
-                self.def_map.root,
-                path,
-                BuiltinShadowMode::Other,
-            );
+        let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]);
 
-            match per_ns.types {
-                Some((ModuleDefId::ModuleId(m), _)) => {
-                    self.def_map.prelude = Some(m);
-                    break;
-                }
-                types => {
-                    tracing::debug!(
-                        "could not resolve prelude path `{}` to module (resolved to {:?})",
-                        path,
-                        types
-                    );
-                }
+        let (per_ns, _) =
+            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);
+            }
+            types => {
+                tracing::debug!(
+                    "could not resolve prelude path `{}` to module (resolved to {:?})",
+                    path.display(self.db.upcast()),
+                    types
+                );
             }
         }
     }
@@ -578,23 +600,29 @@ impl DefCollector<'_> {
         def: ProcMacroDef,
         id: ItemTreeId<item_tree::Function>,
         fn_id: FunctionId,
-        module_id: ModuleId,
     ) {
+        if self.def_map.block.is_some() {
+            return;
+        }
+        let crate_root = self.def_map.module_id(DefMap::ROOT);
+
         let kind = def.kind.to_basedb_kind();
-        let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
-            Some(&(_, expander)) => (expander, kind),
-            None => (ProcMacroExpander::dummy(), kind),
-        };
+        let (expander, kind) =
+            match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
+                Ok(Some(&(_, expander))) => (expander, kind),
+                _ => (ProcMacroExpander::dummy(), kind),
+            };
 
         let proc_macro_id =
-            ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db);
+            ProcMacroLoc { container: crate_root, id, expander, kind }.intern(self.db);
         self.define_proc_macro(def.name.clone(), proc_macro_id);
+        let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
         if let ProcMacroKind::CustomDerive { helpers } = def.kind {
-            self.def_map
+            crate_data
                 .exported_derives
                 .insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers);
         }
-        self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
+        crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
     }
 
     /// Define a macro with `macro_rules`.
@@ -636,7 +664,7 @@ impl DefCollector<'_> {
         // In Rust, `#[macro_export]` macros are unconditionally visible at the
         // crate root, even if the parent modules is **not** visible.
         if export {
-            let module_id = self.def_map.root;
+            let module_id = DefMap::ROOT;
             self.def_map.modules[module_id].scope.declare(macro_.into());
             self.update(
                 module_id,
@@ -687,7 +715,7 @@ impl DefCollector<'_> {
     /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
     /// And unconditionally exported.
     fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) {
-        let module_id = self.def_map.root;
+        let module_id = DefMap::ROOT;
         self.def_map.modules[module_id].scope.declare(macro_.into());
         self.update(
             module_id,
@@ -697,39 +725,28 @@ impl DefCollector<'_> {
         );
     }
 
-    /// Import macros from `#[macro_use] extern crate`.
-    fn import_macros_from_extern_crate(
-        &mut self,
-        current_module_id: LocalModuleId,
-        extern_crate: &item_tree::ExternCrate,
-    ) {
-        tracing::debug!(
-            "importing macros from extern crate: {:?} ({:?})",
-            extern_crate,
-            self.def_map.edition,
-        );
-
-        if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
-            if m == self.def_map.module_id(current_module_id) {
-                cov_mark::hit!(ignore_macro_use_extern_crate_self);
-                return;
-            }
-
-            cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
-            self.import_all_macros_exported(current_module_id, m.krate);
-        }
-    }
-
-    /// Import all exported macros from another crate
+    /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
+    /// macros to be imported. Otherwise this method imports all exported macros.
     ///
     /// 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_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
+    fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
         let def_map = self.db.crate_def_map(krate);
-        for (name, def) in def_map[def_map.root].scope.macros() {
-            // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros.
-            self.define_legacy_macro(current_module_id, name.clone(), def);
+        // `#[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);
+                }
+            }
+        } else {
+            for (name, def) in root_scope.macros() {
+                self.def_map.macro_use_prelude.insert(name.clone(), def);
+            }
         }
     }
 
@@ -762,8 +779,9 @@ impl DefCollector<'_> {
     }
 
     fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
-        let _p = profile::span("resolve_import").detail(|| format!("{}", import.path));
-        tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
+        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 import.is_extern_crate {
             let name = import
                 .path
@@ -785,6 +803,7 @@ impl DefCollector<'_> {
                 module_id,
                 &import.path,
                 BuiltinShadowMode::Module,
+                None, // An import may resolve to any kind of macro.
             );
 
             let def = res.resolved_def;
@@ -815,16 +834,13 @@ impl DefCollector<'_> {
     fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
         if *name == name!(self) {
             cov_mark::hit!(extern_crate_self_as);
-            let root = match self.def_map.block {
-                Some(_) => {
-                    let def_map = self.def_map.crate_root(self.db).def_map(self.db);
-                    def_map.module_id(def_map.root())
-                }
-                None => self.def_map.module_id(self.def_map.root()),
-            };
-            Some(root)
+            Some(self.def_map.crate_root())
         } else {
-            self.deps.get(name).copied()
+            self.deps.get(name).map(|dep| ModuleId {
+                krate: dep.crate_id,
+                block: None,
+                local_id: DefMap::ROOT,
+            })
         }
     }
 
@@ -863,11 +879,14 @@ impl DefCollector<'_> {
                 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
                 if import.is_extern_crate
                     && self.def_map.block.is_none()
-                    && module_id == self.def_map.root
+                    && module_id == DefMap::ROOT
                 {
                     if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
                     {
-                        self.def_map.extern_prelude.insert(name.clone(), def);
+                        Arc::get_mut(&mut self.def_map.data)
+                            .unwrap()
+                            .extern_prelude
+                            .insert(name.clone(), def);
                     }
                 }
 
@@ -1082,7 +1101,14 @@ impl DefCollector<'_> {
             resolved.push((directive.module_id, directive.depth, directive.container, call_id));
         };
         let mut res = ReachedFixedPoint::Yes;
+        // Retain unresolved macros after this round of resolution.
         macros.retain(|directive| {
+            let subns = match &directive.kind {
+                MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
+                MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
+                    MacroSubNs::Attr
+                }
+            };
             let resolver = |path| {
                 let resolved_res = self.def_map.resolve_path_fp_with_macro(
                     self.db,
@@ -1090,6 +1116,7 @@ impl DefCollector<'_> {
                     directive.module_id,
                     &path,
                     BuiltinShadowMode::Module,
+                    Some(subns),
                 );
                 resolved_res
                     .resolved_def
@@ -1101,15 +1128,15 @@ impl DefCollector<'_> {
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
                     let call_id = macro_call_as_call_id(
-                        self.db,
+                        self.db.upcast(),
                         ast_id,
                         *expand_to,
                         self.def_map.krate,
                         resolver_def_id,
-                        &mut |_err| (),
                     );
-                    if let Ok(Ok(call_id)) = call_id {
+                    if let Ok(Some(call_id)) = call_id {
                         push_resolved(directive, call_id);
+
                         res = ReachedFixedPoint::No;
                         return false;
                     }
@@ -1134,7 +1161,7 @@ impl DefCollector<'_> {
                         // Record its helper attributes.
                         if def_id.krate != self.def_map.krate {
                             let def_map = self.db.crate_def_map(def_id.krate);
-                            if let Some(helpers) = def_map.exported_derives.get(&def_id) {
+                            if let Some(helpers) = def_map.data.exported_derives.get(&def_id) {
                                 self.def_map
                                     .derive_helpers_in_scope
                                     .entry(ast_id.ast_id.map(|it| it.upcast()))
@@ -1214,7 +1241,19 @@ impl DefCollector<'_> {
                         };
                         let ast_id = ast_id.with_value(ast_adt_id);
 
-                        match attr.parse_path_comma_token_tree() {
+                        let extend_unhygenic;
+                        let hygiene = if file_id.is_macro() {
+                            self.hygienes
+                                .entry(file_id)
+                                .or_insert_with(|| Hygiene::new(self.db.upcast(), file_id))
+                        } else {
+                            // Avoid heap allocation (`Hygiene` embraces `Arc`) and hash map entry
+                            // when we're in an oridinary (non-macro) file.
+                            extend_unhygenic = Hygiene::new_unhygienic();
+                            &extend_unhygenic
+                        };
+
+                        match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) {
                             Some(derive_macros) => {
                                 let mut len = 0;
                                 for (idx, path) in derive_macros.enumerate() {
@@ -1241,7 +1280,6 @@ impl DefCollector<'_> {
                                     attr,
                                     self.def_map.krate,
                                     def,
-                                    true,
                                 );
                                 self.def_map.modules[directive.module_id]
                                     .scope
@@ -1261,18 +1299,12 @@ impl DefCollector<'_> {
                     }
 
                     // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
-                    let call_id = attr_macro_as_call_id(
-                        self.db,
-                        file_ast_id,
-                        attr,
-                        self.def_map.krate,
-                        def,
-                        false,
-                    );
+                    let call_id =
+                        attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
                     let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
 
                     // If proc attribute macro expansion is disabled, skip expanding it here
-                    if !self.db.enable_proc_attr_macros() {
+                    if !self.db.expand_proc_attr_macros() {
                         self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
                             directive.module_id,
                             loc.kind,
@@ -1345,25 +1377,31 @@ impl DefCollector<'_> {
         let file_id = macro_call_id.as_file();
 
         // First, fetch the raw expansion result for purposes of error reporting. This goes through
-        // `macro_expand_error` to avoid depending on the full expansion result (to improve
+        // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
         // incrementality).
-        let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
-        let err = self.db.macro_expand_error(macro_call_id);
+        let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
         if let Some(err) = err {
+            let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
             let diag = match err {
+                // why is this reported here?
                 hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
                     always!(krate == loc.def.krate);
-                    // Missing proc macros are non-fatal, so they are handled specially.
                     DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
                 }
-                _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
+                _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
             };
 
             self.def_map.diagnostics.push(diag);
         }
+        if let errors @ [_, ..] = &*value {
+            let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
+            let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
+            self.def_map.diagnostics.push(diag);
+        }
 
         // Then, fetch and process the item tree. This will reuse the expansion result from above.
         let item_tree = self.db.file_item_tree(file_id);
+
         let mod_dir = self.mod_dirs[&module_id].clone();
         ModCollector {
             def_collector: &mut *self,
@@ -1384,8 +1422,9 @@ impl DefCollector<'_> {
         for directive in &self.unresolved_macros {
             match &directive.kind {
                 MacroDirectiveKind::FnLike { ast_id, expand_to } => {
+                    // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
                     let macro_call_as_call_id = macro_call_as_call_id(
-                        self.db,
+                        self.db.upcast(),
                         ast_id,
                         *expand_to,
                         self.def_map.krate,
@@ -1396,13 +1435,13 @@ impl DefCollector<'_> {
                                 directive.module_id,
                                 &path,
                                 BuiltinShadowMode::Module,
+                                Some(MacroSubNs::Bang),
                             );
                             resolved_res
                                 .resolved_def
                                 .take_macros()
                                 .map(|it| macro_id_to_def_id(self.db, it))
                         },
-                        &mut |_| (),
                     );
                     if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
                         self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
@@ -1489,6 +1528,7 @@ impl ModCollector<'_, '_> {
 
     fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
         let krate = self.def_collector.def_map.krate;
+        let is_crate_root = self.module_id == DefMap::ROOT;
 
         // Note: don't assert that inserted value is fresh: it's simply not true
         // for macros.
@@ -1496,28 +1536,22 @@ impl ModCollector<'_, '_> {
 
         // Prelude module is always considered to be `#[macro_use]`.
         if let Some(prelude_module) = self.def_collector.def_map.prelude {
-            if prelude_module.krate != krate {
+            if prelude_module.krate != krate && is_crate_root {
                 cov_mark::hit!(prelude_is_macro_use);
-                self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
+                self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
             }
         }
 
         // This should be processed eagerly instead of deferred to resolving.
         // `#[macro_use] extern crate` is hoisted to imports macros before collecting
         // any other items.
-        for &item in items {
-            let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
-            if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
+        //
+        // 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 {
-                    let import = &self.item_tree[id];
-                    let attrs = self.item_tree.attrs(
-                        self.def_collector.db,
-                        krate,
-                        ModItem::from(id).into(),
-                    );
-                    if attrs.by_key("macro_use").exists() {
-                        self.def_collector.import_macros_from_extern_crate(self.module_id, import);
-                    }
+                    self.process_macro_use_extern_crate(id);
                 }
             }
         }
@@ -1610,14 +1644,12 @@ impl ModCollector<'_, '_> {
                         FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
 
                     let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
-                    if self.def_collector.is_proc_macro && self.module_id == def_map.root {
+                    if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT {
                         if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
-                            let crate_root = def_map.module_id(def_map.root);
                             self.def_collector.export_proc_macro(
                                 proc_macro,
                                 ItemTreeId::new(self.tree_id, id),
                                 fn_id,
-                                crate_root,
                             );
                         }
                     }
@@ -1744,6 +1776,52 @@ impl ModCollector<'_, '_> {
         }
     }
 
+    fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+        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) {
+                Some(m) => {
+                    if m == self.def_collector.def_map.module_id(self.module_id) {
+                        cov_mark::hit!(ignore_macro_use_extern_crate_self);
+                        return;
+                    }
+                    m.krate
+                }
+                None => return,
+            };
+
+        cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
+
+        let mut single_imports = Vec::new();
+        let hygiene = Hygiene::new_unhygienic();
+        for attr in attrs.by_key("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);
+                return;
+            };
+            for path in paths {
+                if let Some(name) = path.as_ident() {
+                    single_imports.push(name.clone());
+                }
+            }
+        }
+
+        self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+    }
+
     fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
         let path_attr = attrs.by_key("path").string_value();
         let is_macro_use = attrs.by_key("macro_use").exists();
@@ -1844,7 +1922,6 @@ impl ModCollector<'_, '_> {
         let vis = def_map
             .resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
             .unwrap_or(Visibility::Public);
-        let modules = &mut def_map.modules;
         let origin = match definition {
             None => ModuleOrigin::Inline {
                 definition: declaration,
@@ -1858,6 +1935,7 @@ impl ModCollector<'_, '_> {
             },
         };
 
+        let modules = &mut def_map.modules;
         let res = modules.alloc(ModuleData::new(origin, vis));
         modules[res].parent = Some(self.module_id);
         for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
@@ -1919,7 +1997,10 @@ impl ModCollector<'_, '_> {
             if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
                 continue;
             }
-            tracing::debug!("non-builtin attribute {}", attr.path);
+            tracing::debug!(
+                "non-builtin attribute {}",
+                attr.path.display(self.def_collector.db.upcast())
+            );
 
             let ast_id = AstIdWithPath::new(
                 self.file_id(),
@@ -2053,8 +2134,8 @@ impl ModCollector<'_, '_> {
                         stdx::always!(
                             name == mac.name,
                             "built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
-                            mac.name,
-                            name
+                            mac.name.display(self.def_collector.db.upcast()),
+                            name.display(self.def_collector.db.upcast())
                         );
                         helpers_opt = Some(helpers);
                     }
@@ -2089,73 +2170,60 @@ impl ModCollector<'_, '_> {
             &self.item_tree[mac.visibility],
         );
         if let Some(helpers) = helpers_opt {
-            self.def_collector
-                .def_map
-                .exported_derives
-                .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+            if self.def_collector.def_map.block.is_none() {
+                Arc::get_mut(&mut self.def_collector.def_map.data)
+                    .unwrap()
+                    .exported_derives
+                    .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+            }
         }
     }
 
     fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
         let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+        let db = self.def_collector.db;
+
+        // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
+        // new legacy macros that create textual scopes. We need a way to resolve names in textual
+        // scopes without eager expansion.
 
-        // Case 1: try to resolve in legacy scope and expand macro_rules
-        let mut error = None;
-        match macro_call_as_call_id(
-            self.def_collector.db,
+        // Case 1: try to resolve macro calls with single-segment name and expand macro_rules
+        if let Ok(res) = macro_call_as_call_id(
+            db.upcast(),
             &ast_id,
             mac.expand_to,
             self.def_collector.def_map.krate,
             |path| {
                 path.as_ident().and_then(|name| {
-                    self.def_collector.def_map.with_ancestor_maps(
-                        self.def_collector.db,
-                        self.module_id,
-                        &mut |map, module| {
-                            map[module]
-                                .scope
-                                .get_legacy_macro(name)
-                                .and_then(|it| it.last())
-                                .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
-                        },
-                    )
+                    let def_map = &self.def_collector.def_map;
+                    def_map
+                        .with_ancestor_maps(db, self.module_id, &mut |map, module| {
+                            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())
+                        .filter(|&id| {
+                            sub_namespace_match(
+                                Some(MacroSubNs::from_id(db, id)),
+                                Some(MacroSubNs::Bang),
+                            )
+                        })
+                        .map(|it| macro_id_to_def_id(self.def_collector.db, it))
                 })
             },
-            &mut |err| {
-                error.get_or_insert(err);
-            },
         ) {
-            Ok(Ok(macro_call_id)) => {
-                // Legacy macros need to be expanded immediately, so that any macros they produce
-                // are in scope.
+            // Legacy macros need to be expanded immediately, so that any macros they produce
+            // are in scope.
+            if let Some(val) = res {
                 self.def_collector.collect_macro_expansion(
                     self.module_id,
-                    macro_call_id,
+                    val,
                     self.macro_depth + 1,
                     container,
                 );
-
-                if let Some(err) = error {
-                    self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
-                        self.module_id,
-                        MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
-                        err.to_string(),
-                    ));
-                }
-
-                return;
             }
-            Ok(Err(_)) => {
-                // Built-in macro failed eager expansion.
 
-                self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
-                    self.module_id,
-                    MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
-                    error.unwrap().to_string(),
-                ));
-                return;
-            }
-            Err(UnresolvedMacro { .. }) => (),
+            return;
         }
 
         // Case 2: resolve in module scope, expand during name resolution.
@@ -2215,10 +2283,11 @@ mod tests {
             unresolved_macros: Vec::new(),
             mod_dirs: FxHashMap::default(),
             cfg_options: &CfgOptions::default(),
-            proc_macros: Default::default(),
+            proc_macros: Ok(vec![]),
             from_glob_import: Default::default(),
             skip_attrs: Default::default(),
             is_proc_macro: false,
+            hygienes: FxHashMap::default(),
         };
         collector.seed_with_top_level();
         collector.collect();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index b024d7c6777..18b424255cd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -4,7 +4,10 @@ use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{attrs::AttrId, MacroCallKind};
 use la_arena::Idx;
-use syntax::ast::{self, AnyHasAttrs};
+use syntax::{
+    ast::{self, AnyHasAttrs},
+    SyntaxError,
+};
 
 use crate::{
     item_tree::{self, ItemTreeId},
@@ -29,11 +32,15 @@ pub enum DefDiagnosticKind {
 
     MacroError { ast: MacroCallKind, message: String },
 
+    MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
+
     UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
 
     InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
 
     MalformedDerive { ast: AstId<ast::Adt>, id: usize },
+
+    MacroDefError { ast: AstId<ast::Macro>, message: String },
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -81,7 +88,8 @@ impl DefDiagnostic {
         Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
     }
 
-    pub(super) fn unresolved_proc_macro(
+    // FIXME: Whats the difference between this and unresolved_macro_call
+    pub(crate) fn unresolved_proc_macro(
         container: LocalModuleId,
         ast: MacroCallKind,
         krate: CrateId,
@@ -89,7 +97,7 @@ impl DefDiagnostic {
         Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
     }
 
-    pub(super) fn macro_error(
+    pub(crate) fn macro_error(
         container: LocalModuleId,
         ast: MacroCallKind,
         message: String,
@@ -97,7 +105,22 @@ impl DefDiagnostic {
         Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
     }
 
-    pub(super) fn unresolved_macro_call(
+    pub(crate) fn macro_expansion_parse_error(
+        container: LocalModuleId,
+        ast: MacroCallKind,
+        errors: &[SyntaxError],
+    ) -> Self {
+        Self {
+            in_module: container,
+            kind: DefDiagnosticKind::MacroExpansionParseError {
+                ast,
+                errors: errors.to_vec().into_boxed_slice(),
+            },
+        }
+    }
+
+    // FIXME: Whats the difference between this and unresolved_proc_macro
+    pub(crate) fn unresolved_macro_call(
         container: LocalModuleId,
         ast: MacroCallKind,
         path: ModPath,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
index 51c565fe123..2dcc2c30fe1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
@@ -74,12 +74,20 @@ impl ModDir {
                 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
             }
             None if file_id.is_include_macro(db.upcast()) => {
-                candidate_files.push(format!("{name}.rs"));
-                candidate_files.push(format!("{name}/mod.rs"));
+                candidate_files.push(format!("{}.rs", name.display(db.upcast())));
+                candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
             }
             None => {
-                candidate_files.push(format!("{}{name}.rs", self.dir_path.0));
-                candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0));
+                candidate_files.push(format!(
+                    "{}{}.rs",
+                    self.dir_path.0,
+                    name.display(db.upcast())
+                ));
+                candidate_files.push(format!(
+                    "{}{}/mod.rs",
+                    self.dir_path.0,
+                    name.display(db.upcast())
+                ));
             }
         };
 
@@ -91,7 +99,7 @@ impl ModDir {
                 let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
                     (DirPath::empty(), false)
                 } else {
-                    (DirPath::new(format!("{name}/")), true)
+                    (DirPath::new(format!("{}/", name.display(db.upcast()))), true)
                 };
                 if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
                     return Ok((file_id, is_mod_rs, mod_dir));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 25478481dd0..5f6163175a7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -16,11 +16,11 @@ use hir_expand::name::Name;
 use crate::{
     db::DefDatabase,
     item_scope::BUILTIN_SCOPE,
-    nameres::{BuiltinShadowMode, DefMap},
+    nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
     path::{ModPath, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
-    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
+    AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
 };
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -58,18 +58,22 @@ impl ResolvePathResult {
     }
 }
 
-impl DefMap {
-    pub(super) fn resolve_name_in_extern_prelude(
-        &self,
+impl PerNs {
+    pub(super) fn filter_macro(
+        mut self,
         db: &dyn DefDatabase,
-        name: &Name,
-    ) -> Option<ModuleId> {
-        match self.block {
-            Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
-            None => self.extern_prelude.get(name).copied(),
-        }
+        expected: Option<MacroSubNs>,
+    ) -> Self {
+        self.macros = self.macros.filter(|&(id, _)| {
+            let this = MacroSubNs::from_id(db, id);
+            sub_namespace_match(Some(this), expected)
+        });
+
+        self
     }
+}
 
+impl DefMap {
     pub(crate) fn resolve_visibility(
         &self,
         db: &dyn DefDatabase,
@@ -83,7 +87,7 @@ impl DefMap {
         let mut vis = match visibility {
             RawVisibility::Module(path) => {
                 let (result, remaining) =
-                    self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
+                    self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
                 if remaining.is_some() {
                     return None;
                 }
@@ -106,7 +110,7 @@ impl DefMap {
             // ...unless we're resolving visibility for an associated item in an impl.
             if self.block_id() != m.block && !within_impl {
                 cov_mark::hit!(adjust_vis_in_block_def_map);
-                vis = Visibility::Module(self.module_id(self.root()));
+                vis = Visibility::Module(self.module_id(Self::ROOT));
                 tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
             }
         }
@@ -124,6 +128,9 @@ impl DefMap {
         mut original_module: LocalModuleId,
         path: &ModPath,
         shadow: BuiltinShadowMode,
+        // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're
+        // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths.
+        expected_macro_subns: Option<MacroSubNs>,
     ) -> ResolvePathResult {
         let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
 
@@ -136,6 +143,7 @@ impl DefMap {
                 original_module,
                 path,
                 shadow,
+                expected_macro_subns,
             );
 
             // Merge `new` into `result`.
@@ -154,7 +162,7 @@ impl DefMap {
             match &current_map.block {
                 Some(block) => {
                     original_module = block.parent.local_id;
-                    arc = block.parent.def_map(db);
+                    arc = block.parent.def_map(db, current_map.krate);
                     current_map = &*arc;
                 }
                 None => return result,
@@ -169,11 +177,15 @@ impl DefMap {
         original_module: LocalModuleId,
         path: &ModPath,
         shadow: BuiltinShadowMode,
+        expected_macro_subns: Option<MacroSubNs>,
     ) -> ResolvePathResult {
         let graph = db.crate_graph();
         let _cx = stdx::panic_context::enter(format!(
-            "DefMap {:?} crate_name={:?} block={:?} path={path}",
-            self.krate, graph[self.krate].display_name, self.block
+            "DefMap {:?} crate_name={:?} block={:?} path={}",
+            self.krate,
+            graph[self.krate].display_name,
+            self.block,
+            path.display(db.upcast())
         ));
 
         let mut segments = path.segments().iter().enumerate();
@@ -181,21 +193,21 @@ impl DefMap {
             PathKind::DollarCrate(krate) => {
                 if krate == self.krate {
                     cov_mark::hit!(macro_dollar_crate_self);
-                    PerNs::types(self.crate_root(db).into(), Visibility::Public)
+                    PerNs::types(self.crate_root().into(), Visibility::Public)
                 } else {
                     let def_map = db.crate_def_map(krate);
-                    let module = def_map.module_id(def_map.root);
+                    let module = def_map.module_id(Self::ROOT);
                     cov_mark::hit!(macro_dollar_crate_other);
                     PerNs::types(module.into(), Visibility::Public)
                 }
             }
-            PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
+            PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
             // plain import or absolute path in 2015: crate-relative with
             // fallback to extern prelude (with the simplification in
             // rust-lang/rust#57745)
             // FIXME there must be a nicer way to write this condition
             PathKind::Plain | PathKind::Abs
-                if self.edition == Edition::Edition2015
+                if self.data.edition == Edition::Edition2015
                     && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
             {
                 let (_, segment) = match segments.next() {
@@ -220,7 +232,13 @@ impl DefMap {
                     if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
 
                 tracing::debug!("resolving {:?} in module", segment);
-                self.resolve_name_in_module(db, original_module, segment, prefer_module)
+                self.resolve_name_in_module(
+                    db,
+                    original_module,
+                    segment,
+                    prefer_module,
+                    expected_macro_subns,
+                )
             }
             PathKind::Super(lvl) => {
                 let mut module = original_module;
@@ -236,16 +254,20 @@ impl DefMap {
                                 );
                                 tracing::debug!(
                                     "`super` path: {} -> {} in parent map",
-                                    path,
-                                    new_path
-                                );
-                                return block.parent.def_map(db).resolve_path_fp_with_macro(
-                                    db,
-                                    mode,
-                                    block.parent.local_id,
-                                    &new_path,
-                                    shadow,
+                                    path.display(db.upcast()),
+                                    new_path.display(db.upcast())
                                 );
+                                return block
+                                    .parent
+                                    .def_map(db, self.krate)
+                                    .resolve_path_fp_with_macro(
+                                        db,
+                                        mode,
+                                        block.parent.local_id,
+                                        &new_path,
+                                        shadow,
+                                        expected_macro_subns,
+                                    );
                             }
                             None => {
                                 tracing::debug!("super path in root module");
@@ -271,7 +293,7 @@ impl DefMap {
                     Some((_, segment)) => segment,
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
-                if let Some(&def) = self.extern_prelude.get(segment) {
+                if let Some(&def) = self.data.extern_prelude.get(segment) {
                     tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
                     PerNs::types(def.into(), Visibility::Public)
                 } else {
@@ -303,7 +325,12 @@ impl DefMap {
                         );
                         tracing::debug!("resolving {:?} in other crate", path);
                         let defp_map = module.def_map(db);
-                        let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
+                        // Macro sub-namespaces only matter when resolving single-segment paths
+                        // because `macro_use` and other preludes should be taken into account. At
+                        // this point, we know we're resolving a multi-segment path so macro kind
+                        // expectation is discarded.
+                        let (def, s) =
+                            defp_map.resolve_path(db, module.local_id, &path, shadow, None);
                         return ResolvePathResult::with(
                             def,
                             ReachedFixedPoint::Yes,
@@ -331,11 +358,11 @@ impl DefMap {
                         Some(local_id) => {
                             let variant = EnumVariantId { parent: e, local_id };
                             match &*enum_data.variants[local_id].variant_data {
-                                crate::adt::VariantData::Record(_) => {
+                                crate::data::adt::VariantData::Record(_) => {
                                     PerNs::types(variant.into(), Visibility::Public)
                                 }
-                                crate::adt::VariantData::Tuple(_)
-                                | crate::adt::VariantData::Unit => {
+                                crate::data::adt::VariantData::Tuple(_)
+                                | crate::data::adt::VariantData::Unit => {
                                     PerNs::both(variant.into(), variant.into(), Visibility::Public)
                                 }
                             }
@@ -381,19 +408,24 @@ impl DefMap {
         module: LocalModuleId,
         name: &Name,
         shadow: BuiltinShadowMode,
+        expected_macro_subns: Option<MacroSubNs>,
     ) -> PerNs {
         // Resolve in:
         //  - legacy scope of macro
         //  - current module / scope
-        //  - extern prelude
+        //  - extern prelude / macro_use prelude
         //  - std prelude
         let from_legacy_macro = self[module]
             .scope
             .get_legacy_macro(name)
             // FIXME: shadowing
             .and_then(|it| it.last())
-            .map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public));
-        let from_scope = self[module].scope.get(name);
+            .copied()
+            .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));
+        let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
         let from_builtin = match self.block {
             Some(_) => {
                 // Only resolve to builtins in the root `DefMap`.
@@ -410,13 +442,27 @@ impl DefMap {
         };
 
         let extern_prelude = || {
-            self.extern_prelude
+            if self.block.is_some() {
+                // Don't resolve extern prelude in block `DefMap`s.
+                return PerNs::none();
+            }
+            self.data
+                .extern_prelude
                 .get(name)
                 .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
         };
+        let macro_use_prelude = || {
+            self.macro_use_prelude
+                .get(name)
+                .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+        };
         let prelude = || self.resolve_in_prelude(db, name);
 
-        from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
+        from_legacy_macro
+            .or(from_scope_or_builtin)
+            .or_else(extern_prelude)
+            .or_else(macro_use_prelude)
+            .or_else(prelude)
     }
 
     fn resolve_name_in_crate_root_or_extern_prelude(
@@ -426,13 +472,20 @@ impl DefMap {
     ) -> PerNs {
         let from_crate_root = match self.block {
             Some(_) => {
-                let def_map = self.crate_root(db).def_map(db);
-                def_map[def_map.root].scope.get(name)
+                let def_map = self.crate_root().def_map(db);
+                def_map[Self::ROOT].scope.get(name)
             }
-            None => self[self.root].scope.get(name),
+            None => self[Self::ROOT].scope.get(name),
         };
         let from_extern_prelude = || {
-            self.resolve_name_in_extern_prelude(db, name)
+            if self.block.is_some() {
+                // 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))
         };
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
index caad4a1f381..751b7beaac1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
@@ -52,7 +52,7 @@ impl Attrs {
 }
 
 // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
-// the same strucuture.
+// the same structure.
 #[rustfmt::skip]
 pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
     match tt {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 8a27c60df5c..dd7c3c36306 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -4,10 +4,9 @@ mod macros;
 mod mod_resolution;
 mod primitives;
 
-use std::sync::Arc;
-
 use base_db::{fixture::WithFixture, SourceDatabase};
 use expect_test::{expect, Expect};
+use triomphe::Arc;
 
 use crate::{db::DefDatabase, test_db::TestDB};
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index 13e6825f821..4931c36bbca 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,8 +1,7 @@
-use std::sync::Arc;
-
 use base_db::SourceDatabaseExt;
+use triomphe::Arc;
 
-use crate::{AdtId, ModuleDefId};
+use crate::{db::DefDatabase, AdtId, ModuleDefId};
 
 use super::*;
 
@@ -15,7 +14,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
         });
         assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
     }
-    db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
+    db.set_file_text(pos.file_id, Arc::from(ra_fixture_change));
 
     {
         let events = db.log_executed(|| {
@@ -96,7 +95,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
         });
         assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
     }
-    db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
+    db.set_file_text(pos.file_id, Arc::from("m!(Y);"));
 
     {
         let events = db.log_executed(|| {
@@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
 }
 
 #[test]
-fn typing_inside_a_function_should_not_invalidate_expansions() {
+fn typing_inside_a_function_should_not_invalidate_item_expansions() {
     let (mut db, pos) = TestDB::with_position(
         r#"
 //- /lib.rs
@@ -140,7 +139,7 @@ m!(Z);
         let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
         assert_eq!(n_recalculated_item_trees, 6);
         let n_reparsed_macros =
-            events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+            events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
         assert_eq!(n_reparsed_macros, 3);
     }
 
@@ -150,7 +149,7 @@ fn quux() { 92 }
 m!(Y);
 m!(Z);
 "#;
-    db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
+    db.set_file_text(pos.file_id, Arc::from(new_text));
 
     {
         let events = db.log_executed(|| {
@@ -161,7 +160,7 @@ m!(Z);
         let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
         assert_eq!(n_recalculated_item_trees, 1);
         let n_reparsed_macros =
-            events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+            events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
         assert_eq!(n_reparsed_macros, 0);
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index a4ccd14cbb4..f4cca8d68d0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -260,6 +260,72 @@ mod priv_mod {
 }
 
 #[test]
+fn macro_use_filter() {
+    check(
+        r#"
+//- /main.rs crate:main deps:empty,multiple,all
+#[macro_use()]
+extern crate empty;
+
+foo_not_imported!();
+
+#[macro_use(bar1)]
+#[macro_use()]
+#[macro_use(bar2, bar3)]
+extern crate multiple;
+
+bar1!();
+bar2!();
+bar3!();
+bar_not_imported!();
+
+#[macro_use(baz1)]
+#[macro_use]
+#[macro_use(baz2)]
+extern crate all;
+
+baz1!();
+baz2!();
+baz3!();
+
+//- /empty.rs crate:empty
+#[macro_export]
+macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
+
+//- /multiple.rs crate:multiple
+#[macro_export]
+macro_rules! bar1 { () => { struct OkBar1; } }
+#[macro_export]
+macro_rules! bar2 { () => { struct OkBar2; } }
+#[macro_export]
+macro_rules! bar3 { () => { struct OkBar3; } }
+#[macro_export]
+macro_rules! bar_not_imported { () => { struct NotOkBar; } }
+
+//- /all.rs crate:all
+#[macro_export]
+macro_rules! baz1 { () => { struct OkBaz1; } }
+#[macro_export]
+macro_rules! baz2 { () => { struct OkBaz2; } }
+#[macro_export]
+macro_rules! baz3 { () => { struct OkBaz3; } }
+"#,
+        expect![[r#"
+            crate
+            OkBar1: t v
+            OkBar2: t v
+            OkBar3: t v
+            OkBaz1: t v
+            OkBaz2: t v
+            OkBaz3: t v
+            all: t
+            empty: t
+            multiple: t
+        "#]],
+    );
+}
+
+#[test]
 fn prelude_is_macro_use() {
     cov_mark::check!(prelude_is_macro_use);
     check(
@@ -665,6 +731,29 @@ pub struct bar;
 }
 
 #[test]
+fn macro_dollar_crate_is_correct_in_derive_meta() {
+    let map = compute_crate_def_map(
+        r#"
+//- minicore: derive, clone
+//- /main.rs crate:main deps:lib
+lib::foo!();
+
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! foo {
+    () => {
+        #[derive($crate::Clone)]
+        struct S;
+    }
+}
+
+pub use core::clone::Clone;
+"#,
+    );
+    assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
 fn expand_derive() {
     let map = compute_crate_def_map(
         r#"
@@ -683,7 +772,7 @@ pub macro Copy {}
 pub macro Clone {}
 "#,
     );
-    assert_eq!(map.modules[map.root].scope.impls().len(), 2);
+    assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
 }
 
 #[test]
@@ -726,7 +815,7 @@ pub macro derive($item:item) {}
 pub macro Clone {}
 "#,
     );
-    assert_eq!(map.modules[map.root].scope.impls().len(), 1);
+    assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
 }
 
 #[test]
@@ -991,7 +1080,7 @@ macro_rules! mbe {
 
 #[test]
 fn collects_derive_helpers() {
-    let def_map = compute_crate_def_map(
+    let db = TestDB::with_files(
         r#"
 #![crate_type="proc-macro"]
 struct TokenStream;
@@ -1002,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
 }
 "#,
     );
+    let krate = db.crate_graph().iter().next().unwrap();
+    let def_map = db.crate_def_map(krate);
 
-    assert_eq!(def_map.exported_derives.len(), 1);
-    match def_map.exported_derives.values().next() {
+    assert_eq!(def_map.data.exported_derives.len(), 1);
+    match def_map.data.exported_derives.values().next() {
         Some(helpers) => match &**helpers {
-            [attr] => assert_eq!(attr.to_string(), "helper_attr"),
+            [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
             _ => unreachable!(),
         },
         _ => unreachable!(),
@@ -1169,7 +1260,7 @@ struct A;
 
 #[test]
 fn macro_use_imports_all_macro_types() {
-    let def_map = compute_crate_def_map(
+    let db = TestDB::with_files(
         r#"
 //- /main.rs crate:main deps:lib
 #[macro_use]
@@ -1192,18 +1283,153 @@ struct TokenStream;
 fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
     "#,
     );
+    let krate = db.crate_graph().iter().next().unwrap();
+    let def_map = db.crate_def_map(krate);
+
+    let root_module = &def_map[DefMap::ROOT].scope;
+    assert!(
+        root_module.legacy_macros().count() == 0,
+        "`#[macro_use]` shouldn't bring macros into textual macro scope",
+    );
 
-    let root = &def_map[def_map.root()].scope;
-    let actual = root
-        .legacy_macros()
-        .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0))
-        .map(|(name, _)| format!("{name}\n"))
-        .collect::<String>();
+    let actual = def_map
+        .macro_use_prelude
+        .iter()
+        .map(|(name, _)| name.display(&db).to_string())
+        .sorted()
+        .join("\n");
 
     expect![[r#"
         legacy
         macro20
-        proc_attr
-    "#]]
+        proc_attr"#]]
     .assert_eq(&actual);
 }
+
+#[test]
+fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
+    check(
+        r#"
+//- /lib.rs edition:2021 crate:lib deps:dep,core
+#[macro_use]
+extern crate dep;
+
+macro foo() { struct Ok; }
+macro bar() { fn ok() {} }
+
+foo!();
+bar!();
+
+//- /dep.rs crate:dep
+#[macro_export]
+macro_rules! foo {
+    () => { struct NotOk; }
+}
+
+//- /core.rs crate:core
+pub mod prelude {
+    pub mod rust_2021 {
+        #[macro_export]
+        macro_rules! bar {
+            () => { fn not_ok() {} }
+        }
+    }
+}
+        "#,
+        expect![[r#"
+            crate
+            Ok: t v
+            bar: m
+            dep: t
+            foo: m
+            ok: v
+        "#]],
+    );
+}
+
+#[test]
+fn macro_use_prelude_is_eagerly_expanded() {
+    // See FIXME in `ModCollector::collect_macro_call()`.
+    check(
+        r#"
+//- /main.rs crate:main deps:lib
+#[macro_use]
+extern crate lib;
+mk_foo!();
+mod a {
+    foo!();
+}
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! mk_foo {
+    () => {
+        macro_rules! foo {
+            () => { struct Ok; }
+        }
+    }
+}
+    "#,
+        expect![[r#"
+        crate
+        a: t
+        lib: t
+
+        crate::a
+        Ok: t v
+    "#]],
+    );
+}
+
+#[test]
+fn macro_sub_namespace() {
+    let map = compute_crate_def_map(
+        r#"
+//- minicore: derive, clone
+macro_rules! Clone { () => {} }
+macro_rules! derive { () => {} }
+
+#[derive(Clone)]
+struct S;
+    "#,
+    );
+    assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
+fn macro_sub_namespace2() {
+    check(
+        r#"
+//- /main.rs edition:2021 crate:main deps:proc,core
+use proc::{foo, bar};
+
+foo!();
+bar!();
+
+//- /proc.rs crate:proc
+#![crate_type="proc-macro"]
+#[proc_macro_derive(foo)]
+pub fn foo() {}
+#[proc_macro_attribute]
+pub fn bar() {}
+
+//- /core.rs crate:core
+pub mod prelude {
+    pub mod rust_2021 {
+        pub macro foo() {
+            struct Ok;
+        }
+        pub macro bar() {
+            fn ok() {}
+        }
+    }
+}
+    "#,
+        expect![[r#"
+            crate
+            Ok: t v
+            bar: m
+            foo: m
+            ok: v
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index a0193128847..81bc0ff91e3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -887,7 +887,7 @@ mod module;
 //- /module.rs
 #![cfg(NEVER)]
 
-struct AlsoShoulntAppear;
+struct AlsoShouldNotAppear;
         "#,
         expect![[r#"
             crate
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index f3197d1800f..b9b80825497 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -7,15 +7,14 @@ use std::{
 };
 
 use crate::{
-    body::LowerCtx,
-    type_ref::{ConstRefOrPath, LifetimeRef},
+    lang_item::LangItemTarget,
+    lower::LowerCtx,
+    type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef},
 };
 use hir_expand::name::Name;
 use intern::Interned;
 use syntax::ast;
 
-use crate::type_ref::{TypeBound, TypeRef};
-
 pub use hir_expand::mod_path::{path, ModPath, PathKind};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -36,13 +35,19 @@ impl Display for ImportAlias {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Path {
-    /// Type based path like `<T>::foo`.
-    /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
-    type_anchor: Option<Interned<TypeRef>>,
-    mod_path: Interned<ModPath>,
-    /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
-    generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+pub enum Path {
+    /// A normal path
+    Normal {
+        /// Type based path like `<T>::foo`.
+        /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
+        type_anchor: Option<Interned<TypeRef>>,
+        mod_path: Interned<ModPath>,
+        /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
+        generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+    },
+    /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
+    /// links via a normal path since they might be private and not accessible in the usage place.
+    LangItem(LangItemTarget),
 }
 
 /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -102,51 +107,77 @@ impl Path {
     ) -> Path {
         let generic_args = generic_args.into();
         assert_eq!(path.len(), generic_args.len());
-        Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
+        Path::Normal {
+            type_anchor: None,
+            mod_path: Interned::new(path),
+            generic_args: Some(generic_args),
+        }
+    }
+
+    /// Converts a known mod path to `Path`.
+    pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
+        Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
     }
 
     pub fn kind(&self) -> &PathKind {
-        &self.mod_path.kind
+        match self {
+            Path::Normal { mod_path, .. } => &mod_path.kind,
+            Path::LangItem(_) => &PathKind::Abs,
+        }
     }
 
     pub fn type_anchor(&self) -> Option<&TypeRef> {
-        self.type_anchor.as_deref()
+        match self {
+            Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
+            Path::LangItem(_) => None,
+        }
     }
 
     pub fn segments(&self) -> PathSegments<'_> {
-        let s = PathSegments {
-            segments: self.mod_path.segments(),
-            generic_args: self.generic_args.as_deref(),
+        let Path::Normal { mod_path, generic_args, .. } = self else {
+            return PathSegments {
+                segments: &[],
+                generic_args: None,
+            };
         };
+        let s =
+            PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
         if let Some(generic_args) = s.generic_args {
             assert_eq!(s.segments.len(), generic_args.len());
         }
         s
     }
 
-    pub fn mod_path(&self) -> &ModPath {
-        &self.mod_path
+    pub fn mod_path(&self) -> Option<&ModPath> {
+        match self {
+            Path::Normal { mod_path, .. } => Some(&mod_path),
+            Path::LangItem(_) => None,
+        }
     }
 
     pub fn qualifier(&self) -> Option<Path> {
-        if self.mod_path.is_ident() {
+        let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+            return None;
+        };
+        if mod_path.is_ident() {
             return None;
         }
-        let res = Path {
-            type_anchor: self.type_anchor.clone(),
+        let res = Path::Normal {
+            type_anchor: type_anchor.clone(),
             mod_path: Interned::new(ModPath::from_segments(
-                self.mod_path.kind,
-                self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
+                mod_path.kind,
+                mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
             )),
-            generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
+            generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
         };
         Some(res)
     }
 
     pub fn is_self_type(&self) -> bool {
-        self.type_anchor.is_none()
-            && self.generic_args.as_deref().is_none()
-            && self.mod_path.is_Self()
+        let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+            return false;
+        };
+        type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
     }
 }
 
@@ -222,7 +253,7 @@ impl GenericArgs {
 
 impl From<Name> for Path {
     fn from(name: Name) -> Path {
-        Path {
+        Path::Normal {
             type_anchor: None,
             mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
             generic_args: None,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index b7542bd777d..26d2706175c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -2,17 +2,15 @@
 
 use std::iter;
 
-use crate::type_ref::ConstRefOrPath;
+use crate::{lower::LowerCtx, type_ref::ConstRefOrPath};
 
 use either::Either;
 use hir_expand::name::{name, AsName};
 use intern::Interned;
 use syntax::ast::{self, AstNode, HasTypeBounds};
 
-use super::AssociatedTypeBinding;
 use crate::{
-    body::LowerCtx,
-    path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
+    path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
     type_ref::{LifetimeRef, TypeBound, TypeRef},
 };
 
@@ -75,8 +73,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
                     }
                     // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
                     Some(trait_ref) => {
-                        let Path { mod_path, generic_args: path_generic_args, .. } =
-                            Path::from_src(trait_ref.path()?, ctx)?;
+                        let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
+                            Path::from_src(trait_ref.path()?, ctx)? else
+                        {
+                            return None;
+                        };
                         let num_segments = mod_path.segments().len();
                         kind = mod_path.kind;
 
@@ -157,7 +158,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
     }
 
     let mod_path = Interned::new(ModPath::from_segments(kind, segments));
-    return Some(Path {
+    return Some(Path::Normal {
         type_anchor,
         mod_path,
         generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
@@ -188,6 +189,10 @@ pub(super) fn lower_generic_args(
                 args.push(GenericArg::Type(type_ref));
             }
             ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
+                if assoc_type_arg.param_list().is_some() {
+                    // We currently ignore associated return type bounds.
+                    continue;
+                }
                 if let Some(name_ref) = assoc_type_arg.name_ref() {
                     let name = name_ref.as_name();
                     let args = assoc_type_arg
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 2d45c8c8da1..0aead6f37f7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -2,7 +2,7 @@
 
 use std::fmt::{self, Write};
 
-use hir_expand::mod_path::PathKind;
+use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
 use intern::Interned;
 use itertools::Itertools;
 
@@ -11,11 +11,14 @@ use crate::{
     type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
 };
 
-pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+    if let Path::LangItem(x) = path {
+        return write!(buf, "$lang_item::{x:?}");
+    }
     match path.type_anchor() {
         Some(anchor) => {
             write!(buf, "<")?;
-            print_type_ref(anchor, buf)?;
+            print_type_ref(db, anchor, buf)?;
             write!(buf, ">::")?;
         }
         None => match path.kind() {
@@ -41,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
             write!(buf, "::")?;
         }
 
-        write!(buf, "{}", segment.name)?;
+        write!(buf, "{}", segment.name.display(db))?;
         if let Some(generics) = segment.args_and_bindings {
             write!(buf, "::<")?;
-            print_generic_args(generics, buf)?;
+            print_generic_args(db, generics, buf)?;
 
             write!(buf, ">")?;
         }
@@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
     Ok(())
 }
 
-pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_args(
+    db: &dyn ExpandDatabase,
+    generics: &GenericArgs,
+    buf: &mut dyn Write,
+) -> fmt::Result {
     let mut first = true;
     let args = if generics.has_self_type {
         let (self_ty, args) = generics.args.split_first().unwrap();
         write!(buf, "Self=")?;
-        print_generic_arg(self_ty, buf)?;
+        print_generic_arg(db, self_ty, buf)?;
         first = false;
         args
     } else {
@@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
             write!(buf, ", ")?;
         }
         first = false;
-        print_generic_arg(arg, buf)?;
+        print_generic_arg(db, arg, buf)?;
     }
     for binding in generics.bindings.iter() {
         if !first {
             write!(buf, ", ")?;
         }
         first = false;
-        write!(buf, "{}", binding.name)?;
+        write!(buf, "{}", binding.name.display(db))?;
         if !binding.bounds.is_empty() {
             write!(buf, ": ")?;
-            print_type_bounds(&binding.bounds, buf)?;
+            print_type_bounds(db, &binding.bounds, buf)?;
         }
         if let Some(ty) = &binding.type_ref {
             write!(buf, " = ")?;
-            print_type_ref(ty, buf)?;
+            print_type_ref(db, ty, buf)?;
         }
     }
     Ok(())
 }
 
-pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_arg(
+    db: &dyn ExpandDatabase,
+    arg: &GenericArg,
+    buf: &mut dyn Write,
+) -> fmt::Result {
     match arg {
-        GenericArg::Type(ty) => print_type_ref(ty, buf),
-        GenericArg::Const(c) => write!(buf, "{c}"),
-        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
+        GenericArg::Type(ty) => print_type_ref(db, ty, buf),
+        GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
+        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
     }
 }
 
-pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_type_ref(
+    db: &dyn ExpandDatabase,
+    type_ref: &TypeRef,
+    buf: &mut dyn Write,
+) -> fmt::Result {
     // FIXME: deduplicate with `HirDisplay` impl
     match type_ref {
         TypeRef::Never => write!(buf, "!")?,
@@ -108,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
                 if i != 0 {
                     write!(buf, ", ")?;
                 }
-                print_type_ref(field, buf)?;
+                print_type_ref(db, field, buf)?;
             }
             write!(buf, ")")?;
         }
-        TypeRef::Path(path) => print_path(path, buf)?,
+        TypeRef::Path(path) => print_path(db, path, buf)?,
         TypeRef::RawPtr(pointee, mtbl) => {
             let mtbl = match mtbl {
                 Mutability::Shared => "*const",
                 Mutability::Mut => "*mut",
             };
             write!(buf, "{mtbl} ")?;
-            print_type_ref(pointee, buf)?;
+            print_type_ref(db, pointee, buf)?;
         }
         TypeRef::Reference(pointee, lt, mtbl) => {
             let mtbl = match mtbl {
@@ -128,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
             };
             write!(buf, "&")?;
             if let Some(lt) = lt {
-                write!(buf, "{} ", lt.name)?;
+                write!(buf, "{} ", lt.name.display(db))?;
             }
             write!(buf, "{mtbl}")?;
-            print_type_ref(pointee, buf)?;
+            print_type_ref(db, pointee, buf)?;
         }
         TypeRef::Array(elem, len) => {
             write!(buf, "[")?;
-            print_type_ref(elem, buf)?;
-            write!(buf, "; {len}]")?;
+            print_type_ref(db, elem, buf)?;
+            write!(buf, "; {}]", len.display(db))?;
         }
         TypeRef::Slice(elem) => {
             write!(buf, "[")?;
-            print_type_ref(elem, buf)?;
+            print_type_ref(db, elem, buf)?;
             write!(buf, "]")?;
         }
         TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
@@ -154,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
                 if i != 0 {
                     write!(buf, ", ")?;
                 }
-                print_type_ref(typeref, buf)?;
+                print_type_ref(db, typeref, buf)?;
             }
             if *varargs {
                 if !args.is_empty() {
@@ -163,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
                 write!(buf, "...")?;
             }
             write!(buf, ") -> ")?;
-            print_type_ref(return_type, buf)?;
+            print_type_ref(db, return_type, buf)?;
         }
         TypeRef::Macro(_ast_id) => {
             write!(buf, "<macro>")?;
@@ -171,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
         TypeRef::Error => write!(buf, "{{unknown}}")?,
         TypeRef::ImplTrait(bounds) => {
             write!(buf, "impl ")?;
-            print_type_bounds(bounds, buf)?;
+            print_type_bounds(db, bounds, buf)?;
         }
         TypeRef::DynTrait(bounds) => {
             write!(buf, "dyn ")?;
-            print_type_bounds(bounds, buf)?;
+            print_type_bounds(db, bounds, buf)?;
         }
     }
 
@@ -183,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
 }
 
 pub(crate) fn print_type_bounds(
+    db: &dyn ExpandDatabase,
     bounds: &[Interned<TypeBound>],
     buf: &mut dyn Write,
 ) -> fmt::Result {
@@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds(
                     TraitBoundModifier::None => (),
                     TraitBoundModifier::Maybe => write!(buf, "?")?,
                 }
-                print_path(path, buf)?;
+                print_path(db, path, buf)?;
             }
             TypeBound::ForLifetime(lifetimes, path) => {
-                write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
-                print_path(path, buf)?;
+                write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+                print_path(db, path, buf)?;
             }
-            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
+            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
             TypeBound::Error => write!(buf, "{{unknown}}")?,
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 61e64fc1036..06f5b2526a4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1,5 +1,5 @@
 //! Name resolution façade.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
 
 use base_db::CrateId;
 use hir_expand::name::{name, Name};
@@ -7,16 +7,18 @@ use indexmap::IndexMap;
 use intern::Interned;
 use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
+use triomphe::Arc;
 
 use crate::{
     body::scope::{ExprScopes, ScopeId},
     builtin_type::BuiltinType,
     db::DefDatabase,
-    expr::{BindingId, ExprId, LabelId},
     generics::{GenericParams, TypeOrConstParamData},
+    hir::{BindingId, ExprId, LabelId},
     item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
-    nameres::DefMap,
-    path::{ModPath, PathKind},
+    lang_item::LangItemTarget,
+    nameres::{DefMap, MacroSubNs},
+    path::{ModPath, Path, PathKind},
     per_ns::PerNs,
     visibility::{RawVisibility, Visibility},
     AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
@@ -78,7 +80,7 @@ enum Scope {
     ExprScope(ExprScope),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum TypeNs {
     SelfType(ImplId),
     GenericParam(TypeParamId),
@@ -153,7 +155,8 @@ impl Resolver {
         path: &ModPath,
     ) -> Option<PerNs> {
         let (item_map, module) = self.item_scope();
-        let (module_res, idx) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
+        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?;
@@ -176,8 +179,27 @@ impl Resolver {
     pub fn resolve_path_in_type_ns(
         &self,
         db: &dyn DefDatabase,
-        path: &ModPath,
+        path: &Path,
     ) -> Option<(TypeNs, Option<usize>)> {
+        let path = match path {
+            Path::Normal { mod_path, .. } => mod_path,
+            Path::LangItem(l) => {
+                return Some((
+                    match *l {
+                        LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
+                        LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
+                        LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
+                        LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
+                        LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
+                        LangItemTarget::Trait(x) => TypeNs::TraitId(x),
+                        LangItemTarget::Function(_)
+                        | LangItemTarget::ImplDef(_)
+                        | LangItemTarget::Static(_) => return None,
+                    },
+                    None,
+                ))
+            }
+        };
         let first_name = path.segments().first()?;
         let skip_to_mod = path.kind != PathKind::Plain;
         if skip_to_mod {
@@ -217,7 +239,7 @@ impl Resolver {
     pub fn resolve_path_in_type_ns_fully(
         &self,
         db: &dyn DefDatabase,
-        path: &ModPath,
+        path: &Path,
     ) -> Option<TypeNs> {
         let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
         if unresolved.is_some() {
@@ -245,8 +267,24 @@ impl Resolver {
     pub fn resolve_path_in_value_ns(
         &self,
         db: &dyn DefDatabase,
-        path: &ModPath,
+        path: &Path,
     ) -> Option<ResolveValueResult> {
+        let path = match path {
+            Path::Normal { mod_path, .. } => mod_path,
+            Path::LangItem(l) => {
+                return Some(ResolveValueResult::ValueNs(match *l {
+                    LangItemTarget::Function(x) => ValueNs::FunctionId(x),
+                    LangItemTarget::Static(x) => ValueNs::StaticId(x),
+                    LangItemTarget::Struct(x) => ValueNs::StructId(x),
+                    LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
+                    LangItemTarget::Union(_)
+                    | LangItemTarget::ImplDef(_)
+                    | LangItemTarget::TypeAlias(_)
+                    | LangItemTarget::Trait(_)
+                    | LangItemTarget::EnumId(_) => return None,
+                }))
+            }
+        };
         let n_segments = path.segments().len();
         let tmp = name![self];
         let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
@@ -340,7 +378,7 @@ impl Resolver {
     pub fn resolve_path_in_value_ns_fully(
         &self,
         db: &dyn DefDatabase,
-        path: &ModPath,
+        path: &Path,
     ) -> Option<ValueNs> {
         match self.resolve_path_in_value_ns(db, path)? {
             ResolveValueResult::ValueNs(it) => Some(it),
@@ -348,9 +386,17 @@ impl Resolver {
         }
     }
 
-    pub fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
+    pub fn resolve_path_as_macro(
+        &self,
+        db: &dyn DefDatabase,
+        path: &ModPath,
+        expected_macro_kind: Option<MacroSubNs>,
+    ) -> Option<MacroId> {
         let (item_map, module) = self.item_scope();
-        item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
+        item_map
+            .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
+            .0
+            .take_macros()
     }
 
     /// Returns a set of names available in the current scope.
@@ -415,7 +461,10 @@ impl Resolver {
                 res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
             })
         });
-        def_map.extern_prelude().for_each(|(name, &def)| {
+        def_map.macro_use_prelude().for_each(|(name, def)| {
+            res.add(name, ScopeDef::ModuleDef(def.into()));
+        });
+        def_map.extern_prelude().for_each(|(name, def)| {
             res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
         });
         BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
@@ -441,7 +490,7 @@ impl Resolver {
                 &Scope::ImplDefScope(impl_) => {
                     if let Some(target_trait) = &db.impl_data(impl_).target_trait {
                         if let Some(TypeNs::TraitId(trait_)) =
-                            self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path())
+                            self.resolve_path_in_type_ns_fully(db, &target_trait.path)
                         {
                             traits.insert(trait_);
                         }
@@ -536,15 +585,13 @@ impl Resolver {
                 scope_id,
             }));
             if let Some(block) = expr_scopes.block(scope_id) {
-                if let Some(def_map) = db.block_def_map(block) {
-                    let root = def_map.root();
-                    resolver
-                        .scopes
-                        .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
-                    // FIXME: This adds as many module scopes as there are blocks, but resolving in each
-                    // already traverses all parents, so this is O(n²). I think we could only store the
-                    // innermost module scope instead?
-                }
+                let def_map = db.block_def_map(block);
+                resolver
+                    .scopes
+                    .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
+                // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+                // already traverses all parents, so this is O(n²). I think we could only store the
+                // innermost module scope instead?
             }
         }
 
@@ -592,7 +639,8 @@ impl Resolver {
         shadow: BuiltinShadowMode,
     ) -> PerNs {
         let (item_map, module) = self.item_scope();
-        let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
+        // This method resolves `path` just like import paths, so no expected macro subns is given.
+        let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None);
         if segment_index.is_some() {
             return PerNs::none();
         }
@@ -705,13 +753,11 @@ fn resolver_for_scope_(
 
     for scope in scope_chain.into_iter().rev() {
         if let Some(block) = scopes.block(scope) {
-            if let Some(def_map) = db.block_def_map(block) {
-                let root = def_map.root();
-                r = r.push_block_scope(def_map, root);
-                // FIXME: This adds as many module scopes as there are blocks, but resolving in each
-                // already traverses all parents, so this is O(n²). I think we could only store the
-                // innermost module scope instead?
-            }
+            let def_map = db.block_def_map(block);
+            r = r.push_block_scope(def_map, DefMap::ROOT);
+            // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+            // already traverses all parents, so this is O(n²). I think we could only store the
+            // innermost module scope instead?
         }
 
         r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
@@ -1000,6 +1046,12 @@ impl HasResolver for GenericDefId {
     }
 }
 
+impl HasResolver for EnumVariantId {
+    fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+        self.parent.resolver(db)
+    }
+}
+
 impl HasResolver for VariantId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         match self {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index f69356cac87..6047f770d4d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -20,7 +20,7 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
     fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
         let tree = self.id.item_tree(db);
         let ast_id_map = db.ast_id_map(self.id.file_id());
-        let root = db.parse_or_expand(self.id.file_id()).unwrap();
+        let root = db.parse_or_expand(self.id.file_id());
         let node = &tree[self.id.value];
 
         InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -33,7 +33,7 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
     fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
         let tree = self.id.item_tree(db);
         let ast_id_map = db.ast_id_map(self.id.file_id());
-        let root = db.parse_or_expand(self.id.file_id()).unwrap();
+        let root = db.parse_or_expand(self.id.file_id());
         let node = &tree[self.id.value];
 
         InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -46,7 +46,7 @@ impl HasSource for Macro2Loc {
     fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
         let tree = self.id.item_tree(db);
         let ast_id_map = db.ast_id_map(self.id.file_id());
-        let root = db.parse_or_expand(self.id.file_id()).unwrap();
+        let root = db.parse_or_expand(self.id.file_id());
         let node = &tree[self.id.value];
 
         InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -59,7 +59,7 @@ impl HasSource for MacroRulesLoc {
     fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
         let tree = self.id.item_tree(db);
         let ast_id_map = db.ast_id_map(self.id.file_id());
-        let root = db.parse_or_expand(self.id.file_id()).unwrap();
+        let root = db.parse_or_expand(self.id.file_id());
         let node = &tree[self.id.value];
 
         InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -72,7 +72,7 @@ impl HasSource for ProcMacroLoc {
     fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
         let tree = self.id.item_tree(db);
         let ast_id_map = db.ast_id_map(self.id.file_id());
-        let root = db.parse_or_expand(self.id.file_id()).unwrap();
+        let root = db.parse_or_expand(self.id.file_id());
         let node = &tree[self.id.value];
 
         InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index ee143b19ae5..a6befc8a81a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -1,17 +1,16 @@
 //! Database used for testing `hir_def`.
 
-use std::{
-    fmt, panic,
-    sync::{Arc, Mutex},
-};
+use std::{fmt, panic, sync::Mutex};
 
 use base_db::{
-    salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
-    SourceDatabase, Upcast,
+    salsa::{self, Durability},
+    AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
+    Upcast,
 };
 use hir_expand::{db::ExpandDatabase, InFile};
-use stdx::hash::NoHashHashSet;
+use rustc_hash::FxHashSet;
 use syntax::{algo, ast, AstNode};
+use triomphe::Arc;
 
 use crate::{
     db::DefDatabase,
@@ -35,7 +34,7 @@ pub(crate) struct TestDB {
 impl Default for TestDB {
     fn default() -> Self {
         let mut this = Self { storage: Default::default(), events: Default::default() };
-        this.set_enable_proc_attr_macros(true);
+        this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
         this
     }
 }
@@ -70,13 +69,13 @@ impl fmt::Debug for TestDB {
 impl panic::RefUnwindSafe for TestDB {}
 
 impl FileLoader for TestDB {
-    fn file_text(&self, file_id: FileId) -> Arc<String> {
+    fn file_text(&self, file_id: FileId) -> Arc<str> {
         FileLoaderDelegate(self).file_text(file_id)
     }
     fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
         FileLoaderDelegate(self).resolve_path(path)
     }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+    fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
 }
@@ -111,7 +110,7 @@ impl TestDB {
                 }
                 _ => {
                     // FIXME: handle `mod` inside block expression
-                    return def_map.module_id(def_map.root());
+                    return def_map.module_id(DefMap::ROOT);
                 }
             }
         }
@@ -120,7 +119,7 @@ impl TestDB {
     /// Finds the smallest/innermost module in `def_map` containing `position`.
     fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
         let mut size = None;
-        let mut res = def_map.root();
+        let mut res = DefMap::ROOT;
         for (module, data) in def_map.modules() {
             let src = data.definition_source(self);
             if src.file_id != position.file_id.into() {
@@ -209,13 +208,11 @@ impl TestDB {
             });
 
         for scope in scope_iter {
-            let containing_blocks =
+            let mut containing_blocks =
                 scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
 
-            for block in containing_blocks {
-                if let Some(def_map) = self.block_def_map(block) {
-                    return Some(def_map);
-                }
+            if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) {
+                return Some(block);
             }
         }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
index ab76ed43d3a..30f48de61f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
@@ -1,10 +1,11 @@
 //! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
 
-use std::{iter, sync::Arc};
+use std::iter;
 
 use hir_expand::{hygiene::Hygiene, InFile};
 use la_arena::ArenaMap;
 use syntax::ast;
+use triomphe::Arc;
 
 use crate::{
     db::DefDatabase,