Auto merge of #142294 - GuillaumeGomez:specialize-tostring-on-128-integers, r=tgross35

Use a distinct `ToString` implementation for `u128` and `i128`

Part of https://github.com/rust-lang/rust/issues/135543.

Follow-up of rust-lang/rust#136264.

When working on https://github.com/rust-lang/rust/pull/142098, I realized that `i128` and `u128` could also benefit from a distinct `ToString` implementation so here it.

The last commit is just me realizing that I forgot to add the format tests for `usize` and `isize`.

Here is the bench comparison:

| bench name | last nightly | with this PR | diff |
|-|-|-|-|
| bench_i128 | 29.25 ns/iter (+/- 0.66) | 17.52 ns/iter (+/- 0.7) | -40.1% |
| bench_u128 | 34.06 ns/iter (+/- 0.21) | 16.1 ns/iter (+/- 0.6) | -52.7% |

I used this code to test:

```rust
#![feature(test)]

extern crate test;

use test::{Bencher, black_box};

#[inline(always)]
fn convert_to_string<T: ToString>(n: T) -> String {
    n.to_string()
}

macro_rules! decl_benches {
    ($($name:ident: $ty:ident,)+) => {
        $(
	    #[bench]
            fn $name(c: &mut Bencher) {
                c.iter(|| convert_to_string(black_box({ let nb: $ty = 20; nb })));
            }
	)+
    }
}

decl_benches! {
    bench_u128: u128,
    bench_i128: i128,
}
```
diff --git a/.typos.toml b/.typos.toml
index e938bdd..cdbc003 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -18,6 +18,7 @@
     "INOUT",
     "optin",
     "=Pn",
+    "\\[[0-9A-F]{4},",  # AstId hex hashes
     # ignore `// spellchecker:off` until `// spellchecker:on`
     "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on",
 ]
diff --git a/Cargo.lock b/Cargo.lock
index 01de430..2c7b464 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,9 +13,9 @@
 
 [[package]]
 name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
 
 [[package]]
 name = "allocator-api2"
@@ -25,9 +25,9 @@
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
 [[package]]
 name = "arbitrary"
@@ -100,51 +100,68 @@
 
 [[package]]
 name = "bitflags"
-version = "2.9.0"
+version = "2.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
 
 [[package]]
 name = "borsh"
-version = "1.5.5"
+version = "1.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5430e3be710b68d984d1391c854eb431a9d548640711faa54eecb1df93db91cc"
+checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce"
 dependencies = [
  "cfg_aliases",
 ]
 
 [[package]]
 name = "boxcar"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66bb12751a83493ef4b8da1120451a262554e216a247f14b48cb5e8fe7ed8bdf"
+checksum = "26c4925bc979b677330a8c7fe7a8c94af2dbb4a2d37b4a20a80d884400f46baa"
 
 [[package]]
 name = "camino"
-version = "1.1.9"
+version = "1.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
+checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "cargo-platform"
-version = "0.1.9"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
+checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4"
 dependencies = [
  "serde",
 ]
 
 [[package]]
-name = "cargo_metadata"
-version = "0.19.2"
+name = "cargo-util-schemas"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
+checksum = "e63d2780ac94487eb9f1fea7b0d56300abc9eb488800854ca217f102f5caccca"
+dependencies = [
+ "semver",
+ "serde",
+ "serde-untagged",
+ "serde-value",
+ "thiserror 1.0.69",
+ "toml",
+ "unicode-xid",
+ "url",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502"
 dependencies = [
  "camino",
  "cargo-platform",
+ "cargo-util-schemas",
  "semver",
  "serde",
  "serde_json",
@@ -153,9 +170,9 @@
 
 [[package]]
 name = "cc"
-version = "1.2.16"
+version = "1.2.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
+checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
 dependencies = [
  "shlex",
 ]
@@ -178,9 +195,9 @@
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "cfg_aliases"
@@ -190,9 +207,9 @@
 
 [[package]]
 name = "chalk-derive"
-version = "0.102.0"
+version = "0.103.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "feb14e3ff0ebac26d8e58b6ed1417afb60c4a0a44b6425546ee7eb9c75ebb336"
+checksum = "eb4899682de915ca7c0b025bdd0a3d34c75fe12184122fda6805a7baddaa293c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -202,19 +219,19 @@
 
 [[package]]
 name = "chalk-ir"
-version = "0.102.0"
+version = "0.103.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72f0a61621a088af69fee8df39ec63cf5b6d0b9ab663a740cdeb376aabf2f244"
+checksum = "90a37d2ab99352b4caca135061e7b4ac67024b648c28ed0b787feec4bea4caed"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "chalk-derive",
 ]
 
 [[package]]
 name = "chalk-recursive"
-version = "0.102.0"
+version = "0.103.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbd3415cc540015533aa4a8ad007696d585dd9c5f81e7c099872f1dd4bf14894"
+checksum = "c855be60e646664bc37c2496d3dc81ca5ef60520930e5e0f0057a0575aff6c19"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -225,9 +242,9 @@
 
 [[package]]
 name = "chalk-solve"
-version = "0.102.0"
+version = "0.103.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "747707b0c082b3ecf4b1ae28d0d8df708a46cddd22a386f9cc85a312a4de25ff"
+checksum = "477ac6cdfd2013e9f93b09b036c2b607a67b2e728f4777b8422d55a79e9e3a34"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -374,7 +391,7 @@
  "libc",
  "option-ext",
  "redox_users",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -432,6 +449,16 @@
 checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
 
 [[package]]
+name = "erased-serde"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
+dependencies = [
+ "serde",
+ "typeid",
+]
+
+[[package]]
 name = "expect-test"
 version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -461,9 +488,9 @@
 
 [[package]]
 name = "flate2"
-version = "1.1.1"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -471,9 +498,9 @@
 
 [[package]]
 name = "foldhash"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
 
 [[package]]
 name = "form_urlencoded"
@@ -501,9 +528,9 @@
 
 [[package]]
 name = "getrandom"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
 dependencies = [
  "cfg-if",
  "libc",
@@ -524,9 +551,9 @@
 
 [[package]]
 name = "hashbrown"
-version = "0.15.2"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
 dependencies = [
  "allocator-api2",
  "equivalent",
@@ -539,7 +566,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
 dependencies = [
- "hashbrown 0.15.2",
+ "hashbrown 0.15.4",
 ]
 
 [[package]]
@@ -550,9 +577,9 @@
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.9"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 
 [[package]]
 name = "hir"
@@ -588,7 +615,7 @@
 dependencies = [
  "arrayvec",
  "base-db",
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "cfg",
  "cov-mark",
  "drop_bomb",
@@ -655,7 +682,7 @@
 dependencies = [
  "arrayvec",
  "base-db",
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "chalk-derive",
  "chalk-ir",
  "chalk-recursive",
@@ -705,21 +732,22 @@
 
 [[package]]
 name = "icu_collections"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
 dependencies = [
  "displaydoc",
+ "potential_utf",
  "yoke",
  "zerofrom",
  "zerovec",
 ]
 
 [[package]]
-name = "icu_locid"
-version = "1.5.0"
+name = "icu_locale_core"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
 dependencies = [
  "displaydoc",
  "litemap",
@@ -729,30 +757,10 @@
 ]
 
 [[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
-
-[[package]]
 name = "icu_normalizer"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
 dependencies = [
  "displaydoc",
  "icu_collections",
@@ -760,68 +768,55 @@
  "icu_properties",
  "icu_provider",
  "smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
  "zerovec",
 ]
 
 [[package]]
 name = "icu_normalizer_data"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
 
 [[package]]
 name = "icu_properties"
-version = "1.5.1"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
 dependencies = [
  "displaydoc",
  "icu_collections",
- "icu_locid_transform",
+ "icu_locale_core",
  "icu_properties_data",
  "icu_provider",
- "tinystr",
+ "potential_utf",
+ "zerotrie",
  "zerovec",
 ]
 
 [[package]]
 name = "icu_properties_data"
-version = "1.5.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
 
 [[package]]
 name = "icu_provider"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
 dependencies = [
  "displaydoc",
- "icu_locid",
- "icu_provider_macros",
+ "icu_locale_core",
  "stable_deref_trait",
  "tinystr",
  "writeable",
  "yoke",
  "zerofrom",
+ "zerotrie",
  "zerovec",
 ]
 
 [[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "ide"
 version = "0.0.0"
 dependencies = [
@@ -898,7 +893,7 @@
 dependencies = [
  "arrayvec",
  "base-db",
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "cov-mark",
  "crossbeam-channel",
  "either",
@@ -976,9 +971,9 @@
 
 [[package]]
 name = "idna_adapter"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
 dependencies = [
  "icu_normalizer",
  "icu_properties",
@@ -991,7 +986,7 @@
 checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
 dependencies = [
  "equivalent",
- "hashbrown 0.15.2",
+ "hashbrown 0.15.4",
  "serde",
 ]
 
@@ -1001,7 +996,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "inotify-sys",
  "libc",
 ]
@@ -1057,9 +1052,9 @@
 
 [[package]]
 name = "kqueue"
-version = "1.0.8"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
+checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
 dependencies = [
  "kqueue-sys",
  "libc",
@@ -1099,19 +1094,19 @@
 
 [[package]]
 name = "libloading"
-version = "0.8.7"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
+checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
 dependencies = [
  "cfg-if",
- "windows-targets 0.53.0",
+ "windows-targets 0.53.2",
 ]
 
 [[package]]
 name = "libmimalloc-sys"
-version = "0.1.40"
+version = "0.1.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07d0e07885d6a754b9c7993f2625187ad694ee985d60f23355ff0e7077261502"
+checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4"
 dependencies = [
  "cc",
  "libc",
@@ -1123,7 +1118,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "libc",
  "redox_syscall",
 ]
@@ -1149,9 +1144,9 @@
 
 [[package]]
 name = "litemap"
-version = "0.7.5"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
 
 [[package]]
 name = "load-cargo"
@@ -1174,9 +1169,9 @@
 
 [[package]]
 name = "lock_api"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
 dependencies = [
  "autocfg",
  "scopeguard",
@@ -1184,9 +1179,9 @@
 
 [[package]]
 name = "log"
-version = "0.4.26"
+version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
 name = "lsp-server"
@@ -1249,9 +1244,9 @@
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
 
 [[package]]
 name = "memmap2"
@@ -1273,32 +1268,32 @@
 
 [[package]]
 name = "mimalloc"
-version = "0.1.44"
+version = "0.1.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99585191385958383e13f6b822e6b6d8d9cf928e7d286ceb092da92b43c87bc1"
+checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af"
 dependencies = [
  "libmimalloc-sys",
 ]
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.5"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
 dependencies = [
  "adler2",
 ]
 
 [[package]]
 name = "mio"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
 dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1316,7 +1311,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "cfg-if",
  "cfg_aliases",
  "libc",
@@ -1334,7 +1329,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "filetime",
  "fsevent-sys",
  "inotify",
@@ -1369,10 +1364,19 @@
 checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
 
 [[package]]
-name = "num_cpus"
-version = "1.16.0"
+name = "num-traits"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
 dependencies = [
  "hermit-abi",
  "libc",
@@ -1398,9 +1402,9 @@
 
 [[package]]
 name = "once_cell"
-version = "1.21.1"
+version = "1.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
 name = "oorandom"
@@ -1415,10 +1419,19 @@
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
 [[package]]
-name = "parking_lot"
-version = "0.12.3"
+name = "ordered-float"
+version = "2.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -1426,9 +1439,9 @@
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.10"
+version = "0.9.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
 dependencies = [
  "cfg-if",
  "libc",
@@ -1445,7 +1458,7 @@
  "edition",
  "expect-test",
  "ra-ap-rustc_lexer",
- "rustc-literal-escaper",
+ "rustc-literal-escaper 0.0.3",
  "stdx",
  "tracing",
 ]
@@ -1506,9 +1519,18 @@
 
 [[package]]
 name = "portable-atomic"
-version = "1.11.0"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
 
 [[package]]
 name = "powerfmt"
@@ -1569,9 +1591,9 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -1596,7 +1618,7 @@
  "libc",
  "perf-event",
  "tikv-jemalloc-ctl",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -1650,7 +1672,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "memchr",
  "unicase",
 ]
@@ -1687,11 +1709,11 @@
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c33b8fa229789975647ca5426be432c7c327ebde89ab15889928185dbcee3230"
+checksum = "a967e3a9cd3e38b543f503978e0eccee461e3aea3f7b10e944959bff41dbe612"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "ra-ap-rustc_hashes",
  "ra-ap-rustc_index",
  "tracing",
@@ -1699,18 +1721,18 @@
 
 [[package]]
 name = "ra-ap-rustc_hashes"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d68a3e389927002f552938a90b04787f6435f55b46fc5691360470d1cb2e99d"
+checksum = "1ea4c755ecbbffa5743c251344f484ebe571ec7bc5b36d80b2a8ae775d1a7a40"
 dependencies = [
  "rustc-stable-hash",
 ]
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32502273df2838d0ca13f1c67e2a48feef940e591f9771869f07e2db2acede53"
+checksum = "aca7ad7cf911538c619caa2162339fe98637e9e46f11bb0484ef96735df4d64a"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1718,9 +1740,9 @@
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a32f081864ae34c7ae6634edfa7a95ab9260ba85015e8b1d347580eda79d14f"
+checksum = "8767ba551c9355bc3031be072cc4bb0381106e5e7cd275e72b7a8c76051c4070"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1729,9 +1751,9 @@
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed34c51974718c5bd90d876d1364d9725159fc8030c2382b9cb837034152ed68"
+checksum = "6101374afb267e6c27e4e2eb0b1352e9f3504c1a8f716f619cd39244e2ed92ab"
 dependencies = [
  "memchr",
  "unicode-properties",
@@ -1740,19 +1762,19 @@
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff0440e5d27facbf4ff13ea651e48c2f6e360b3dbfc56251b41d60719b965fb8"
+checksum = "ecd88a19f00da4f43e6727d5013444cbc399804b5046dfa2bbcd28ebed3970ce"
 dependencies = [
  "ra-ap-rustc_lexer",
- "rustc-literal-escaper",
+ "rustc-literal-escaper 0.0.2",
 ]
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.113.0"
+version = "0.116.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6056efa57aba3aa0cc69a0bf1a8281624c23ad25b05748d11ebcd4668037bfc"
+checksum = "bb332dd32d7850a799862533b1c021e6062558861a4ad57817bf522499fbb892"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.1.1",
@@ -1783,11 +1805,11 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.10"
+version = "0.5.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
+checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
 ]
 
 [[package]]
@@ -1874,16 +1896,16 @@
  "vfs",
  "vfs-notify",
  "walkdir",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
  "xflags",
  "xshell",
 ]
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.24"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
 
 [[package]]
 name = "rustc-hash"
@@ -1904,6 +1926,12 @@
 checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
 
 [[package]]
+name = "rustc-literal-escaper"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78744cd17f5d01c75b709e49807d1363e02a940ccee2e9e72435843fdb0d076e"
+
+[[package]]
 name = "rustc-stable-hash"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1911,11 +1939,11 @@
 
 [[package]]
 name = "rustc_apfloat"
-version = "0.2.2+llvm-462a31f5a5ab"
+version = "0.2.3+llvm-462a31f5a5ab"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "121e2195ff969977a4e2b5c9965ea867fce7e4cb5aee5b09dee698a7932d574f"
+checksum = "486c2179b4796f65bfe2ee33679acf0927ac83ecf583ad6c91c3b4570911b9ad"
 dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.9.1",
  "smallvec",
 ]
 
@@ -1934,7 +1962,7 @@
  "boxcar",
  "crossbeam-queue",
  "dashmap",
- "hashbrown 0.15.2",
+ "hashbrown 0.15.4",
  "hashlink",
  "indexmap",
  "parking_lot",
@@ -2016,6 +2044,27 @@
 ]
 
 [[package]]
+name = "serde-untagged"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e"
+dependencies = [
+ "erased-serde",
+ "serde",
+ "typeid",
+]
+
+[[package]]
+name = "serde-value"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
+dependencies = [
+ "ordered-float",
+ "serde",
+]
+
+[[package]]
 name = "serde_derive"
 version = "1.0.219"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2052,9 +2101,9 @@
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.8"
+version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
 dependencies = [
  "serde",
 ]
@@ -2076,9 +2125,9 @@
 
 [[package]]
 name = "smallvec"
-version = "1.14.0"
+version = "1.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
 
 [[package]]
 name = "smol_str"
@@ -2122,14 +2171,14 @@
  "libc",
  "miow",
  "tracing",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
 name = "syn"
-version = "2.0.100"
+version = "2.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2138,9 +2187,9 @@
 
 [[package]]
 name = "synstructure"
-version = "0.13.1"
+version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2158,7 +2207,7 @@
  "rayon",
  "rowan",
  "rustc-hash 2.1.1",
- "rustc-literal-escaper",
+ "rustc-literal-escaper 0.0.3",
  "rustc_apfloat",
  "smol_str",
  "stdx",
@@ -2183,9 +2232,9 @@
 
 [[package]]
 name = "tenthash"
-version = "1.0.0"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d092d622df8bb64e5de8dc86a3667702d5f1e0fe2f0604c6035540703c8cd1e"
+checksum = "e5c4bcc0a4fa333239f43662d15fbf995f384b2aeaf89c4ab4c83353d6cbb952"
 
 [[package]]
 name = "test-fixture"
@@ -2270,12 +2319,11 @@
 
 [[package]]
 name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
 dependencies = [
  "cfg-if",
- "once_cell",
 ]
 
 [[package]]
@@ -2311,9 +2359,9 @@
 
 [[package]]
 name = "time"
-version = "0.3.40"
+version = "0.3.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
 dependencies = [
  "deranged",
  "itoa",
@@ -2334,9 +2382,9 @@
 
 [[package]]
 name = "time-macros"
-version = "0.2.21"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
 dependencies = [
  "num-conv",
  "time-core",
@@ -2344,9 +2392,9 @@
 
 [[package]]
 name = "tinystr"
-version = "0.7.6"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
 dependencies = [
  "displaydoc",
  "zerovec",
@@ -2354,9 +2402,9 @@
 
 [[package]]
 name = "toml"
-version = "0.8.20"
+version = "0.8.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -2366,27 +2414,34 @@
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.8"
+version = "0.6.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.24"
+version = "0.22.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
 dependencies = [
  "indexmap",
  "serde",
  "serde_spanned",
  "toml_datetime",
+ "toml_write",
  "winnow",
 ]
 
 [[package]]
+name = "toml_write"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
+
+[[package]]
 name = "toolchain"
 version = "0.0.0"
 dependencies = [
@@ -2407,9 +2462,9 @@
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.28"
+version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2418,9 +2473,9 @@
 
 [[package]]
 name = "tracing-core"
-version = "0.1.33"
+version = "0.1.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
 dependencies = [
  "once_cell",
  "valuable",
@@ -2486,6 +2541,12 @@
 checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
 
 [[package]]
+name = "typeid"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
+
+[[package]]
 name = "ungrammar"
 version = "1.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2528,12 +2589,6 @@
 ]
 
 [[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
-[[package]]
 name = "utf8_iter"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2586,9 +2641,9 @@
 
 [[package]]
 name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
 [[package]]
 name = "winapi-util"
@@ -2601,9 +2656,9 @@
 
 [[package]]
 name = "windows"
-version = "0.61.1"
+version = "0.61.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
+checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
 dependencies = [
  "windows-collections",
  "windows-core",
@@ -2623,9 +2678,9 @@
 
 [[package]]
 name = "windows-core"
-version = "0.61.0"
+version = "0.61.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
 dependencies = [
  "windows-implement",
  "windows-interface",
@@ -2636,12 +2691,13 @@
 
 [[package]]
 name = "windows-future"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
+checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
 dependencies = [
  "windows-core",
  "windows-link",
+ "windows-threading",
 ]
 
 [[package]]
@@ -2668,9 +2724,9 @@
 
 [[package]]
 name = "windows-link"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
 
 [[package]]
 name = "windows-numerics"
@@ -2684,18 +2740,18 @@
 
 [[package]]
 name = "windows-result"
-version = "0.3.2"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
 dependencies = [
  "windows-link",
 ]
 
 [[package]]
 name = "windows-strings"
-version = "0.4.0"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
 dependencies = [
  "windows-link",
 ]
@@ -2728,6 +2784,15 @@
 ]
 
 [[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.2",
+]
+
+[[package]]
 name = "windows-targets"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2760,9 +2825,9 @@
 
 [[package]]
 name = "windows-targets"
-version = "0.53.0"
+version = "0.53.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
 dependencies = [
  "windows_aarch64_gnullvm 0.53.0",
  "windows_aarch64_msvc 0.53.0",
@@ -2775,6 +2840,15 @@
 ]
 
 [[package]]
+name = "windows-threading"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2914,9 +2988,9 @@
 
 [[package]]
 name = "winnow"
-version = "0.7.3"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
+checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
 dependencies = [
  "memchr",
 ]
@@ -2928,16 +3002,10 @@
 checksum = "23f6174b2566cc4a74f95e1367ec343e7fa80c93cc8087f5c4a3d6a1088b2118"
 
 [[package]]
-name = "write16"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
-
-[[package]]
 name = "writeable"
-version = "0.5.5"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
 
 [[package]]
 name = "xflags"
@@ -2992,9 +3060,9 @@
 
 [[package]]
 name = "yoke"
-version = "0.7.5"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
 dependencies = [
  "serde",
  "stable_deref_trait",
@@ -3004,9 +3072,9 @@
 
 [[package]]
 name = "yoke-derive"
-version = "0.7.5"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3036,10 +3104,21 @@
 ]
 
 [[package]]
-name = "zerovec"
-version = "0.10.4"
+name = "zerotrie"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
 dependencies = [
  "yoke",
  "zerofrom",
@@ -3048,9 +3127,9 @@
 
 [[package]]
 name = "zerovec-derive"
-version = "0.10.3"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3059,9 +3138,9 @@
 
 [[package]]
 name = "zip"
-version = "3.0.0"
+version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308"
+checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd"
 dependencies = [
  "arbitrary",
  "crc32fast",
diff --git a/Cargo.toml b/Cargo.toml
index 975fe27..449c758 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -87,11 +87,11 @@
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 edition = { path = "./crates/edition", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.113", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.113", default-features = false }
-ra-ap-rustc_index = { version = "0.113", default-features = false }
-ra-ap-rustc_abi = { version = "0.113", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.113", default-features = false }
+ra-ap-rustc_lexer = { version = "0.116", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.116", default-features = false }
+ra-ap-rustc_index = { version = "0.116", default-features = false }
+ra-ap-rustc_abi = { version = "0.116", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.116", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 
@@ -101,24 +101,24 @@
 lsp-server = { version = "0.7.8" }
 
 # non-local crates
-anyhow = "1.0.97"
+anyhow = "1.0.98"
 arrayvec = "0.7.6"
-bitflags = "2.9.0"
-cargo_metadata = "0.19.2"
-camino = "1.1.9"
-chalk-solve = { version = "0.102.0", default-features = false }
-chalk-ir = "0.102.0"
-chalk-recursive = { version = "0.102.0", default-features = false }
-chalk-derive = "0.102.0"
+bitflags = "2.9.1"
+cargo_metadata = "0.20.0"
+camino = "1.1.10"
+chalk-solve = { version = "0.103.0", default-features = false }
+chalk-ir = "0.103.0"
+chalk-recursive = { version = "0.103.0", default-features = false }
+chalk-derive = "0.103.0"
 crossbeam-channel = "0.5.15"
 dissimilar = "1.0.10"
 dot = "0.1.4"
 either = "1.15.0"
 expect-test = "1.5.1"
-indexmap = { version = "2.8.0", features = ["serde"] }
+indexmap = { version = "2.9.0", features = ["serde"] }
 itertools = "0.14.0"
-libc = "0.2.171"
-libloading = "0.8.6"
+libc = "0.2.172"
+libloading = "0.8.8"
 memmap2 = "0.9.5"
 nohash-hasher = "0.2.0"
 oorandom = "11.1.5"
@@ -129,20 +129,22 @@
   "macho",
   "pe",
 ] }
-process-wrap = { version = "8.2.0", features = ["std"] }
+process-wrap = { version = "8.2.1", features = ["std"] }
 pulldown-cmark-to-cmark = "10.0.4"
 pulldown-cmark = { version = "0.9.6", default-features = false }
 rayon = "1.10.0"
 rowan = "=0.15.15"
-salsa = { version = "0.22.0", default-features = false, features = ["rayon","salsa_unstable"] }
+# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
+# on impls without it
+salsa = { version = "0.22.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
 salsa-macros = "0.22.0"
 semver = "1.0.26"
 serde = { version = "1.0.219" }
 serde_derive = { version = "1.0.219" }
 serde_json = "1.0.140"
 rustc-hash = "2.1.1"
-rustc-literal-escaper = "0.0.2"
-smallvec = { version = "1.14.0", features = [
+rustc-literal-escaper = "0.0.3"
+smallvec = { version = "1.15.1", features = [
   "const_new",
   "union",
   "const_generics",
@@ -166,7 +168,7 @@
 # We need to freeze the version of the crate, as the raw-api feature is considered unstable
 dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] }
 # We need to freeze the version of the crate, as it needs to match with dashmap
-hashbrown = { version = "0.14.0", features = [
+hashbrown = { version = "0.14.*", features = [
   "inline-more",
 ], default-features = false }
 
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 7452381..2a87b15 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -20,12 +20,10 @@
 use triomphe::Arc;
 use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet};
 
-use crate::{CrateWorkspaceData, EditionedFileId, RootQueryDb};
+use crate::{CrateWorkspaceData, EditionedFileId, FxIndexSet, RootQueryDb};
 
 pub type ProcMacroPaths = FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), String>>;
 
-type FxIndexSet<T> = indexmap::IndexSet<T, FxBuildHasher>;
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct SourceRootId(pub u32);
 
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 4d4e6ca..478fae6 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -28,6 +28,8 @@
 use triomphe::Arc;
 pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
 
+pub type FxIndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>;
+
 #[macro_export]
 macro_rules! impl_intern_key {
     ($id:ident, $loc:ident) => {
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index c1c89e8..c6922ec 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -25,7 +25,7 @@
 tracing.workspace = true
 smallvec.workspace = true
 triomphe.workspace = true
-rustc_apfloat = "0.2.2"
+rustc_apfloat = "0.2.3"
 text-size.workspace = true
 salsa.workspace = true
 salsa-macros.workspace = true
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index bb6222b..b509e69 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -14,6 +14,7 @@
 use la_arena::{ArenaMap, Idx, RawIdx};
 use mbe::DelimiterKind;
 use rustc_abi::ReprOptions;
+use span::AstIdNode;
 use syntax::{
     AstPtr,
     ast::{self, HasAttrs},
@@ -22,10 +23,10 @@
 use tt::iter::{TtElement, TtIter};
 
 use crate::{
-    AdtId, AttrDefId, GenericParamId, HasModule, ItemTreeLoc, LocalFieldId, Lookup, MacroId,
+    AdtId, AstIdLoc, AttrDefId, GenericParamId, HasModule, LocalFieldId, Lookup, MacroId,
     VariantId,
     db::DefDatabase,
-    item_tree::{AttrOwner, FieldParent, ItemTreeNode},
+    item_tree::block_item_tree_query,
     lang_item::LangItem,
     nameres::{ModuleOrigin, ModuleSource},
     src::{HasChildSource, HasSource},
@@ -42,6 +43,15 @@
 }
 
 impl Attrs {
+    pub fn new(
+        db: &dyn DefDatabase,
+        owner: &dyn ast::HasAttrs,
+        span_map: SpanMapRef<'_>,
+        cfg_options: &CfgOptions,
+    ) -> Self {
+        Attrs(RawAttrs::new_expanded(db, owner, span_map, cfg_options))
+    }
+
     pub fn get(&self, id: AttrId) -> Option<&Attr> {
         (**self).iter().find(|attr| attr.id == id)
     }
@@ -94,44 +104,64 @@
         v: VariantId,
     ) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
         let _p = tracing::info_span!("fields_attrs_query").entered();
-        // FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
         let mut res = ArenaMap::default();
-        let item_tree;
-        let (parent, fields, krate) = match v {
+        let (fields, file_id, krate) = match v {
             VariantId::EnumVariantId(it) => {
                 let loc = it.lookup(db);
                 let krate = loc.parent.lookup(db).container.krate;
-                item_tree = loc.id.item_tree(db);
-                let variant = &item_tree[loc.id.value];
-                (FieldParent::EnumVariant(loc.id.value), &variant.fields, krate)
+                let source = loc.source(db);
+                (source.value.field_list(), source.file_id, krate)
             }
             VariantId::StructId(it) => {
                 let loc = it.lookup(db);
                 let krate = loc.container.krate;
-                item_tree = loc.id.item_tree(db);
-                let struct_ = &item_tree[loc.id.value];
-                (FieldParent::Struct(loc.id.value), &struct_.fields, krate)
+                let source = loc.source(db);
+                (source.value.field_list(), source.file_id, krate)
             }
             VariantId::UnionId(it) => {
                 let loc = it.lookup(db);
                 let krate = loc.container.krate;
-                item_tree = loc.id.item_tree(db);
-                let union_ = &item_tree[loc.id.value];
-                (FieldParent::Union(loc.id.value), &union_.fields, krate)
+                let source = loc.source(db);
+                (
+                    source.value.record_field_list().map(ast::FieldList::RecordFieldList),
+                    source.file_id,
+                    krate,
+                )
             }
         };
+        let Some(fields) = fields else {
+            return Arc::new(res);
+        };
 
         let cfg_options = krate.cfg_options(db);
+        let span_map = db.span_map(file_id);
 
-        let mut idx = 0;
-        for (id, _field) in fields.iter().enumerate() {
-            let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, id));
-            if attrs.is_cfg_enabled(cfg_options) {
-                res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
-                idx += 1;
+        match fields {
+            ast::FieldList::RecordFieldList(fields) => {
+                let mut idx = 0;
+                for field in fields.fields() {
+                    let attrs =
+                        Attrs(RawAttrs::new_expanded(db, &field, span_map.as_ref(), cfg_options));
+                    if attrs.is_cfg_enabled(cfg_options).is_ok() {
+                        res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
+                        idx += 1;
+                    }
+                }
+            }
+            ast::FieldList::TupleFieldList(fields) => {
+                let mut idx = 0;
+                for field in fields.fields() {
+                    let attrs =
+                        Attrs(RawAttrs::new_expanded(db, &field, span_map.as_ref(), cfg_options));
+                    if attrs.is_cfg_enabled(cfg_options).is_ok() {
+                        res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
+                        idx += 1;
+                    }
+                }
             }
         }
 
+        res.shrink_to_fit();
         Arc::new(res)
     }
 }
@@ -167,11 +197,10 @@
     }
 
     #[inline]
-    pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
-        match self.cfg() {
-            None => true,
-            Some(cfg) => cfg_options.check(&cfg) != Some(false),
-        }
+    pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Result<(), CfgExpr> {
+        self.cfgs().try_for_each(|cfg| {
+            if cfg_options.check(&cfg) != Some(false) { Ok(()) } else { Err(cfg) }
+        })
     }
 
     #[inline]
@@ -488,61 +517,59 @@
     pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
         let _p = tracing::info_span!("attrs_query").entered();
         // FIXME: this should use `Trace` to avoid duplication in `source_map` below
-        let raw_attrs = match def {
+        match def {
             AttrDefId::ModuleId(module) => {
                 let def_map = module.def_map(db);
                 let mod_data = &def_map[module.local_id];
 
-                match mod_data.origin {
-                    ModuleOrigin::File { definition, declaration_tree_id, .. } => {
+                let raw_attrs = match mod_data.origin {
+                    ModuleOrigin::File { definition, declaration_tree_id, declaration, .. } => {
                         let decl_attrs = declaration_tree_id
                             .item_tree(db)
-                            .raw_attrs(AttrOwner::ModItem(declaration_tree_id.value.into()))
+                            .raw_attrs(declaration.upcast())
                             .clone();
                         let tree = db.file_item_tree(definition.into());
-                        let def_attrs = tree.raw_attrs(AttrOwner::TopLevel).clone();
+                        let def_attrs = tree.top_level_raw_attrs().clone();
                         decl_attrs.merge(def_attrs)
                     }
                     ModuleOrigin::CrateRoot { definition } => {
                         let tree = db.file_item_tree(definition.into());
-                        tree.raw_attrs(AttrOwner::TopLevel).clone()
+                        tree.top_level_raw_attrs().clone()
                     }
-                    ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id
-                        .item_tree(db)
-                        .raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into()))
-                        .clone(),
+                    ModuleOrigin::Inline { definition_tree_id, definition } => {
+                        definition_tree_id.item_tree(db).raw_attrs(definition.upcast()).clone()
+                    }
                     ModuleOrigin::BlockExpr { id, .. } => {
-                        let tree = db.block_item_tree(id);
-                        tree.raw_attrs(AttrOwner::TopLevel).clone()
+                        let tree = block_item_tree_query(db, id);
+                        tree.top_level_raw_attrs().clone()
                     }
-                }
+                };
+                Attrs::expand_cfg_attr(db, module.krate, raw_attrs)
             }
-            AttrDefId::FieldId(it) => {
-                return db.fields_attrs(it.parent)[it.local_id].clone();
-            }
-            AttrDefId::EnumVariantId(it) => attrs_from_item_tree_loc(db, it),
+            AttrDefId::FieldId(it) => db.fields_attrs(it.parent)[it.local_id].clone(),
+            AttrDefId::EnumVariantId(it) => attrs_from_ast_id_loc(db, it),
             AttrDefId::AdtId(it) => match it {
-                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),
+                AdtId::StructId(it) => attrs_from_ast_id_loc(db, it),
+                AdtId::EnumId(it) => attrs_from_ast_id_loc(db, it),
+                AdtId::UnionId(it) => attrs_from_ast_id_loc(db, it),
             },
-            AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
+            AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::TraitAliasId(it) => attrs_from_ast_id_loc(db, it),
             AttrDefId::MacroId(it) => match it {
-                MacroId::Macro2Id(it) => attrs_from_item_tree_loc(db, it),
-                MacroId::MacroRulesId(it) => attrs_from_item_tree_loc(db, it),
-                MacroId::ProcMacroId(it) => attrs_from_item_tree_loc(db, it),
+                MacroId::Macro2Id(it) => attrs_from_ast_id_loc(db, it),
+                MacroId::MacroRulesId(it) => attrs_from_ast_id_loc(db, it),
+                MacroId::ProcMacroId(it) => attrs_from_ast_id_loc(db, it),
             },
-            AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::ConstId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::StaticId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::FunctionId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::TypeAliasId(it) => attrs_from_item_tree_loc(db, it),
+            AttrDefId::ImplId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::ConstId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::StaticId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::FunctionId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::TypeAliasId(it) => attrs_from_ast_id_loc(db, it),
             AttrDefId::GenericParamId(it) => match it {
                 GenericParamId::ConstParamId(it) => {
                     let src = it.parent().child_source(db);
                     // FIXME: We should be never getting `None` here.
-                    return Attrs(match src.value.get(it.local_id()) {
+                    Attrs(match src.value.get(it.local_id()) {
                         Some(val) => RawAttrs::new_expanded(
                             db,
                             val,
@@ -550,12 +577,12 @@
                             def.krate(db).cfg_options(db),
                         ),
                         None => RawAttrs::EMPTY,
-                    });
+                    })
                 }
                 GenericParamId::TypeParamId(it) => {
                     let src = it.parent().child_source(db);
                     // FIXME: We should be never getting `None` here.
-                    return Attrs(match src.value.get(it.local_id()) {
+                    Attrs(match src.value.get(it.local_id()) {
                         Some(val) => RawAttrs::new_expanded(
                             db,
                             val,
@@ -563,12 +590,12 @@
                             def.krate(db).cfg_options(db),
                         ),
                         None => RawAttrs::EMPTY,
-                    });
+                    })
                 }
                 GenericParamId::LifetimeParamId(it) => {
                     let src = it.parent.child_source(db);
                     // FIXME: We should be never getting `None` here.
-                    return Attrs(match src.value.get(it.local_id) {
+                    Attrs(match src.value.get(it.local_id) {
                         Some(val) => RawAttrs::new_expanded(
                             db,
                             val,
@@ -576,16 +603,13 @@
                             def.krate(db).cfg_options(db),
                         ),
                         None => RawAttrs::EMPTY,
-                    });
+                    })
                 }
             },
-            AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::ExternCrateId(it) => attrs_from_item_tree_loc(db, it),
-            AttrDefId::UseId(it) => attrs_from_item_tree_loc(db, it),
-        };
-
-        let attrs = raw_attrs.expand_cfg_attr(db, def.krate(db));
-        Attrs(attrs)
+            AttrDefId::ExternBlockId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::ExternCrateId(it) => attrs_from_ast_id_loc(db, it),
+            AttrDefId::UseId(it) => attrs_from_ast_id_loc(db, it),
+        }
     }
 
     pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@@ -787,14 +811,15 @@
     id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
 }
 
-fn attrs_from_item_tree_loc<'db, N: ItemTreeNode>(
+fn attrs_from_ast_id_loc<'db, N: AstIdNode + HasAttrs>(
     db: &(dyn DefDatabase + 'db),
-    lookup: impl Lookup<Database = dyn DefDatabase, Data = impl ItemTreeLoc<Id = N>>,
-) -> RawAttrs {
-    let id = lookup.lookup(db).item_tree_id();
-    let tree = id.item_tree(db);
-    let attr_owner = N::attr_owner(id.value);
-    tree.raw_attrs(attr_owner).clone()
+    lookup: impl Lookup<Database = dyn DefDatabase, Data = impl AstIdLoc<Ast = N> + HasModule>,
+) -> Attrs {
+    let loc = lookup.lookup(db);
+    let source = loc.source(db);
+    let span_map = db.span_map(source.file_id);
+    let cfg_options = loc.krate(db).cfg_options(db);
+    Attrs(RawAttrs::new_expanded(db, &source.value, span_map.as_ref(), cfg_options))
 }
 
 pub(crate) fn fields_attrs_source_map(
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 4a9a3b1..c618e4b 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -1,18 +1,20 @@
 //! Defines database & queries for name resolution.
 use base_db::{Crate, RootQueryDb, SourceDatabase};
 use either::Either;
-use hir_expand::{EditionedFileId, HirFileId, MacroCallId, MacroDefId, db::ExpandDatabase};
+use hir_expand::{
+    EditionedFileId, HirFileId, InFile, Lookup, MacroCallId, MacroDefId, MacroDefKind,
+    db::ExpandDatabase,
+};
 use intern::sym;
 use la_arena::ArenaMap;
 use syntax::{AstPtr, ast};
-use thin_vec::ThinVec;
 use triomphe::Arc;
 
 use crate::{
-    AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, EnumVariantId,
+    AssocItemId, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, EnumVariantId,
     EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId,
-    FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroId,
-    MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId,
+    FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander,
+    MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId,
     StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId,
     TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId,
     attr::{Attrs, AttrsWithOwner},
@@ -21,17 +23,13 @@
     },
     hir::generics::GenericParams,
     import_map::ImportMap,
-    item_tree::{AttrOwner, ItemTree},
+    item_tree::{ItemTree, file_item_tree_query},
     lang_item::{self, LangItem},
-    nameres::{
-        assoc::{ImplItems, TraitItems},
-        crate_def_map,
-        diagnostics::DefDiagnostics,
-    },
+    nameres::{assoc::TraitItems, crate_def_map, diagnostics::DefDiagnostics},
     signatures::{
-        ConstSignature, EnumSignature, EnumVariants, FunctionSignature, ImplSignature,
-        InactiveEnumVariantCode, StaticSignature, StructSignature, TraitAliasSignature,
-        TraitSignature, TypeAliasSignature, UnionSignature, VariantFields,
+        ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature,
+        StructSignature, TraitAliasSignature, TraitSignature, TypeAliasSignature, UnionSignature,
+        VariantFields,
     },
     tt,
     visibility::{self, Visibility},
@@ -93,9 +91,6 @@
     #[salsa::interned]
     fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
     // // endregion: items
-
-    #[salsa::interned]
-    fn intern_block(&self, loc: BlockLoc) -> BlockId;
 }
 
 #[query_group::query_group]
@@ -105,11 +100,9 @@
     fn expand_proc_attr_macros(&self) -> bool;
 
     /// Computes an [`ItemTree`] for the given file or macro expansion.
-    #[salsa::invoke(ItemTree::file_item_tree_query)]
-    fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
-
-    #[salsa::invoke(ItemTree::block_item_tree_query)]
-    fn block_item_tree(&self, block_id: BlockId) -> Arc<ItemTree>;
+    #[salsa::invoke(file_item_tree_query)]
+    #[salsa::transparent]
+    fn file_item_tree(&self, file_id: HirFileId) -> &ItemTree;
 
     /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution.
     #[salsa::invoke(macro_def)]
@@ -123,24 +116,6 @@
         id: VariantId,
     ) -> (Arc<VariantFields>, Arc<ExpressionStoreSourceMap>);
 
-    #[salsa::tracked]
-    fn enum_variants(&self, id: EnumId) -> Arc<EnumVariants> {
-        self.enum_variants_with_diagnostics(id).0
-    }
-
-    #[salsa::invoke(EnumVariants::enum_variants_query)]
-    fn enum_variants_with_diagnostics(
-        &self,
-        id: EnumId,
-    ) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>);
-
-    #[salsa::transparent]
-    #[salsa::invoke(ImplItems::impl_items_query)]
-    fn impl_items(&self, e: ImplId) -> Arc<ImplItems>;
-
-    #[salsa::invoke(ImplItems::impl_items_with_diagnostics_query)]
-    fn impl_items_with_diagnostics(&self, e: ImplId) -> (Arc<ImplItems>, DefDiagnostics);
-
     #[salsa::transparent]
     #[salsa::invoke(TraitItems::trait_items_query)]
     fn trait_items(&self, e: TraitId) -> Arc<TraitItems>;
@@ -323,16 +298,8 @@
     #[salsa::invoke(visibility::field_visibilities_query)]
     fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
 
-    // FIXME: unify function_visibility and const_visibility?
-
-    #[salsa::invoke(visibility::function_visibility_query)]
-    fn function_visibility(&self, def: FunctionId) -> Visibility;
-
-    #[salsa::invoke(visibility::const_visibility_query)]
-    fn const_visibility(&self, def: ConstId) -> Visibility;
-
-    #[salsa::invoke(visibility::type_alias_visibility_query)]
-    fn type_alias_visibility(&self, def: TypeAliasId) -> Visibility;
+    #[salsa::invoke(visibility::assoc_visibility_query)]
+    fn assoc_visibility(&self, def: AssocItemId) -> Visibility;
 
     // endregion:visibilities
 
@@ -368,7 +335,7 @@
 fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: Crate) -> bool {
     let file = crate_id.data(db).root_file_id(db);
     let item_tree = db.file_item_tree(file.into());
-    let attrs = item_tree.raw_attrs(AttrOwner::TopLevel);
+    let attrs = item_tree.top_level_raw_attrs();
     for attr in &**attrs {
         match attr.path().as_ident() {
             Some(ident) if *ident == sym::no_std => return true,
@@ -399,10 +366,6 @@
 }
 
 fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
-    use hir_expand::InFile;
-
-    use crate::{Lookup, MacroDefKind, MacroExpander};
-
     let kind = |expander, file_id, m| {
         let in_file = InFile::new(file_id, m);
         match expander {
@@ -418,11 +381,9 @@
         MacroId::Macro2Id(it) => {
             let loc: Macro2Loc = it.lookup(db);
 
-            let item_tree = loc.id.item_tree(db);
-            let makro = &item_tree[loc.id.value];
             MacroDefId {
                 krate: loc.container.krate,
-                kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()),
+                kind: kind(loc.expander, loc.id.file_id, loc.id.value.upcast()),
                 local_inner: false,
                 allow_internal_unsafe: loc.allow_internal_unsafe,
                 edition: loc.edition,
@@ -431,11 +392,9 @@
         MacroId::MacroRulesId(it) => {
             let loc: MacroRulesLoc = it.lookup(db);
 
-            let item_tree = loc.id.item_tree(db);
-            let makro = &item_tree[loc.id.value];
             MacroDefId {
                 krate: loc.container.krate,
-                kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()),
+                kind: kind(loc.expander, loc.id.file_id, loc.id.value.upcast()),
                 local_inner: loc.flags.contains(MacroRulesLocFlags::LOCAL_INNER),
                 allow_internal_unsafe: loc
                     .flags
@@ -446,15 +405,9 @@
         MacroId::ProcMacroId(it) => {
             let loc = it.lookup(db);
 
-            let item_tree = loc.id.item_tree(db);
-            let makro = &item_tree[loc.id.value];
             MacroDefId {
                 krate: loc.container.krate,
-                kind: MacroDefKind::ProcMacro(
-                    InFile::new(loc.id.file_id(), makro.ast_id),
-                    loc.expander,
-                    loc.kind,
-                ),
+                kind: MacroDefKind::ProcMacro(loc.id, loc.expander, loc.kind),
                 local_inner: false,
                 allow_internal_unsafe: false,
                 edition: loc.edition,
diff --git a/crates/hir-def/src/expr_store/expander.rs b/crates/hir-def/src/expr_store/expander.rs
index 3823fb5..23b9712 100644
--- a/crates/hir-def/src/expr_store/expander.rs
+++ b/crates/hir-def/src/expr_store/expander.rs
@@ -6,6 +6,7 @@
 use cfg::CfgOptions;
 use drop_bomb::DropBomb;
 use hir_expand::AstId;
+use hir_expand::span_map::SpanMapRef;
 use hir_expand::{
     ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
     eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
@@ -223,9 +224,15 @@
         }
     }
 
+    #[inline]
     pub(super) fn ast_id_map(&self) -> &AstIdMap {
         &self.ast_id_map
     }
+
+    #[inline]
+    pub(super) fn span_map(&self) -> SpanMapRef<'_> {
+        self.span_map.as_ref()
+    }
 }
 
 #[derive(Debug)]
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 29871f5..03683ec 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -7,12 +7,14 @@
 
 use std::mem;
 
+use base_db::FxIndexSet;
 use cfg::CfgOptions;
 use either::Either;
 use hir_expand::{
-    HirFileId, InFile, Lookup, MacroDefId,
+    HirFileId, InFile, Intern, MacroDefId,
     mod_path::tool_path,
     name::{AsName, Name},
+    span_map::SpanMapRef,
 };
 use intern::{Symbol, sym};
 use rustc_hash::FxHashMap;
@@ -30,8 +32,8 @@
 use tt::TextRange;
 
 use crate::{
-    AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemTreeLoc,
-    MacroId, ModuleDefId, ModuleId, TraitAliasId, TraitId, TypeAliasId, UnresolvedMacro,
+    AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
+    ModuleDefId, ModuleId, TraitAliasId, TraitId, TypeAliasId, UnresolvedMacro,
     builtin_type::BuiltinUint,
     db::DefDatabase,
     expr_store::{
@@ -65,8 +67,6 @@
 
 pub use self::path::hir_segment_to_ast_segment;
 
-type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
-
 pub(super) fn lower_body(
     db: &dyn DefDatabase,
     owner: DefWithBodyId,
@@ -564,6 +564,11 @@
         }
     }
 
+    #[inline]
+    pub(crate) fn span_map(&self) -> SpanMapRef<'_> {
+        self.expander.span_map()
+    }
+
     pub fn lower_lifetime_ref(&mut self, lifetime: ast::Lifetime) -> LifetimeRefId {
         // FIXME: Keyword check?
         let lifetime_ref = match &*lifetime.text() {
@@ -2141,26 +2146,10 @@
         block: ast::BlockExpr,
         mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
     ) -> ExprId {
-        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(_)))
-                || (block.may_carry_attributes() && block.attrs().next().is_some())
-        };
-
-        let block_id = if block_has_items {
-            let file_local_id = self.expander.ast_id_map().ast_id(&block);
+        let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
             let ast_id = self.expander.in_file(file_local_id);
-            Some(self.db.intern_block(BlockLoc { ast_id, module: self.module }))
-        } else {
-            None
-        };
+            BlockLoc { ast_id, module: self.module }.intern(self.db)
+        });
 
         let (module, def_map) =
             match block_id.map(|block_id| (block_def_map(self.db, block_id), block_id)) {
@@ -2260,11 +2249,8 @@
                     match resolved.take_values() {
                         Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
                         Some(ModuleDefId::EnumVariantId(variant))
-                            if {
-                                let loc = variant.lookup(self.db);
-                                let tree = loc.item_tree_id().item_tree(self.db);
-                                tree[loc.id.value].shape != FieldsShape::Record
-                            } =>
+                        // FIXME: This can cause a cycle if the user is writing invalid code
+                            if self.db.variant_fields(variant.into()).shape != FieldsShape::Record =>
                         {
                             (None, Pat::Path(name.into()))
                         }
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index f12a9b7..56c7655 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -9,9 +9,10 @@
 use hir_expand::{Lookup, mod_path::PathKind};
 use itertools::Itertools;
 use span::Edition;
+use syntax::ast::HasName;
 
 use crate::{
-    AdtId, DefWithBodyId, GenericDefId, ItemTreeLoc, TypeParamId, VariantId,
+    AdtId, DefWithBodyId, GenericDefId, TypeParamId, VariantId,
     expr_store::path::{GenericArg, GenericArgs},
     hir::{
         Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
@@ -19,6 +20,7 @@
     },
     lang_item::LangItemTarget,
     signatures::{FnFlags, FunctionSignature, StructSignature},
+    src::HasSource,
     type_ref::{ConstRef, LifetimeRef, Mutability, TraitBoundModifier, TypeBound, UseArgRef},
 };
 use crate::{LifetimeParamId, signatures::StructFlags};
@@ -48,6 +50,17 @@
     Indentation,
 }
 
+fn item_name<Id, Loc>(db: &dyn DefDatabase, id: Id, default: &str) -> String
+where
+    Id: Lookup<Database = dyn DefDatabase, Data = Loc>,
+    Loc: HasSource,
+    Loc::Value: ast::HasName,
+{
+    let loc = id.lookup(db);
+    let source = loc.source(db);
+    source.value.name().map_or_else(|| default.to_owned(), |name| name.to_string())
+}
+
 pub fn print_body_hir(
     db: &dyn DefDatabase,
     body: &Body,
@@ -55,31 +68,14 @@
     edition: Edition,
 ) -> String {
     let header = match owner {
-        DefWithBodyId::FunctionId(it) => {
-            it.lookup(db).id.resolved(db, |it| format!("fn {}", it.name.display(db, edition)))
-        }
-        DefWithBodyId::StaticId(it) => it
-            .lookup(db)
-            .id
-            .resolved(db, |it| format!("static {} = ", it.name.display(db, edition))),
-        DefWithBodyId::ConstId(it) => it.lookup(db).id.resolved(db, |it| {
-            format!(
-                "const {} = ",
-                match &it.name {
-                    Some(name) => name.display(db, edition).to_string(),
-                    None => "_".to_owned(),
-                }
-            )
-        }),
-        DefWithBodyId::VariantId(it) => {
-            let loc = it.lookup(db);
-            let enum_loc = loc.parent.lookup(db);
-            format!(
-                "enum {}::{}",
-                enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition),
-                loc.id.item_tree(db)[loc.id.value].name.display(db, edition),
-            )
-        }
+        DefWithBodyId::FunctionId(it) => format!("fn {}", item_name(db, it, "<missing>")),
+        DefWithBodyId::StaticId(it) => format!("static {} = ", item_name(db, it, "<missing>")),
+        DefWithBodyId::ConstId(it) => format!("const {} = ", item_name(db, it, "_")),
+        DefWithBodyId::VariantId(it) => format!(
+            "enum {}::{}",
+            item_name(db, it.lookup(db).parent, "<missing>"),
+            item_name(db, it, "<missing>")
+        ),
     };
 
     let mut p = Printer {
@@ -116,22 +112,13 @@
 
 pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: Edition) -> String {
     let header = match owner {
-        VariantId::StructId(it) => {
-            it.lookup(db).id.resolved(db, |it| format!("struct {}", it.name.display(db, edition)))
-        }
-        VariantId::EnumVariantId(enum_variant_id) => {
-            let loc = enum_variant_id.lookup(db);
-            let enum_loc = loc.parent.lookup(db);
-            format!(
-                "enum {}::{}",
-                enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition),
-                loc.id.item_tree(db)[loc.id.value].name.display(db, edition),
-            )
-        }
-        VariantId::UnionId(union_id) => union_id
-            .lookup(db)
-            .id
-            .resolved(db, |it| format!("union {}", it.name.display(db, edition))),
+        VariantId::StructId(it) => format!("struct {}", item_name(db, it, "<missing>")),
+        VariantId::EnumVariantId(it) => format!(
+            "enum {}::{}",
+            item_name(db, it.lookup(db).parent, "<missing>"),
+            item_name(db, it, "<missing>")
+        ),
+        VariantId::UnionId(it) => format!("union {}", item_name(db, it, "<missing>")),
     };
 
     let fields = db.variant_fields(owner);
@@ -154,9 +141,11 @@
         let FieldData { name, type_ref, visibility, is_unsafe } = data;
         match visibility {
             crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => {
-                w!(p, "{}", interned.display(db, p.edition))
+                w!(p, "pub(in {})", interned.display(db, p.edition))
             }
             crate::item_tree::RawVisibility::Public => w!(p, "pub "),
+            crate::item_tree::RawVisibility::PubCrate => w!(p, "pub(crate) "),
+            crate::item_tree::RawVisibility::PubSelf(_) => w!(p, "pub(self) "),
         }
         if *is_unsafe {
             w!(p, "unsafe ");
@@ -1089,10 +1078,7 @@
             w!(self, "builtin#lang(");
             macro_rules! write_name {
                 ($it:ident) => {{
-                    let loc = $it.lookup(self.db);
-                    let tree = loc.item_tree_id().item_tree(self.db);
-                    let name = &tree[loc.id.value].name;
-                    w!(self, "{}", name.display(self.db, self.edition));
+                    w!(self, "{}", item_name(self.db, $it, "<missing>"));
                 }};
             }
             match *it {
diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs
index 5f7b510..bb0b70b 100644
--- a/crates/hir-def/src/expr_store/tests/body/block.rs
+++ b/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -189,8 +189,8 @@
 }
     "#,
         expect![[r#"
-            BlockId(3c01) in BlockRelativeModuleId { block: Some(BlockId(3c00)), local_id: Idx::<ModuleData>(1) }
-            BlockId(3c00) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
+            BlockIdLt { [salsa id]: Id(3c01) } in BlockRelativeModuleId { block: Some(BlockIdLt { [salsa id]: Id(3c00) }), local_id: Idx::<ModuleData>(1) }
+            BlockIdLt { [salsa id]: Id(3c00) } in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
             crate scope
         "#]],
     );
@@ -397,7 +397,6 @@
 fn underscore_import() {
     // This used to panic, because the default (private) visibility inside block expressions would
     // point into the containing `DefMap`, which visibilities should never be able to do.
-    cov_mark::check!(adjust_vis_in_block_def_map);
     check_at(
         r#"
 mod m {
@@ -457,7 +456,6 @@
 #[test]
 fn is_visible_from_same_def_map() {
     // Regression test for https://github.com/rust-lang/rust-analyzer/issues/9481
-    cov_mark::check!(is_visible_from_same_block_def_map);
     check_at(
         r#"
 fn outer() {
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index bb75621..dccfff0 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -137,7 +137,7 @@
         let loc = variant.lookup(ctx.db);
         if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) {
             path.push_segment(
-                ctx.db.enum_variants(loc.parent).variants[loc.index as usize].1.clone(),
+                loc.parent.enum_variants(ctx.db).variants[loc.index as usize].1.clone(),
             );
             return Some(path);
         }
@@ -615,6 +615,7 @@
                         cov_mark::hit!(discount_private_imports);
                         false
                     }
+                    Visibility::PubCrate(_) => true,
                     Visibility::Public => true,
                 };
 
@@ -1286,7 +1287,6 @@
 
     #[test]
     fn explicit_private_imports_crate() {
-        cov_mark::check!(explicit_private_imports);
         check_found_path(
             r#"
 //- /main.rs
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 5362c05..efa4399 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -13,13 +13,14 @@
 use span::Edition;
 use stdx::format_to;
 use syntax::ast;
+use thin_vec::ThinVec;
 
 use crate::{
     AdtId, BuiltinType, ConstId, ExternBlockId, ExternCrateId, FxIndexMap, HasModule, ImplId,
     LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, UseId,
     db::DefDatabase,
     per_ns::{Item, MacrosItem, PerNs, TypesItem, ValuesItem},
-    visibility::{Visibility, VisibilityExplicitness},
+    visibility::Visibility,
 };
 
 #[derive(Debug, Default)]
@@ -155,22 +156,21 @@
 
     /// The defs declared in this scope. Each def has a single scope where it is
     /// declared.
-    declarations: Vec<ModuleDefId>,
+    declarations: ThinVec<ModuleDefId>,
 
-    impls: Vec<ImplId>,
-    #[allow(clippy::box_collection)]
-    extern_blocks: Option<Box<Vec<ExternBlockId>>>,
-    unnamed_consts: Vec<ConstId>,
+    impls: ThinVec<ImplId>,
+    extern_blocks: ThinVec<ExternBlockId>,
+    unnamed_consts: ThinVec<ConstId>,
     /// Traits imported via `use Trait as _;`.
-    unnamed_trait_imports: FxHashMap<TraitId, Item<()>>,
+    unnamed_trait_imports: ThinVec<(TraitId, Item<()>)>,
 
     // the resolutions of the imports of this scope
     use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
     use_imports_values: FxHashMap<ImportOrGlob, ImportOrDef>,
     use_imports_macros: FxHashMap<ImportOrExternCrate, ImportOrDef>,
 
-    use_decls: Vec<UseId>,
-    extern_crate_decls: Vec<ExternCrateId>,
+    use_decls: ThinVec<UseId>,
+    extern_crate_decls: ThinVec<ExternCrateId>,
     /// Macros visible in current module in legacy textual scope
     ///
     /// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first.
@@ -183,7 +183,7 @@
     /// Module scoped macros will be inserted into `items` instead of here.
     // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
     // be all resolved to the last one defined if shadowing happens.
-    legacy_macros: FxHashMap<Name, SmallVec<[MacroId; 1]>>,
+    legacy_macros: FxHashMap<Name, SmallVec<[MacroId; 2]>>,
     /// The attribute macro invocations in this scope.
     attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
     /// The macro invocations in this scope.
@@ -198,7 +198,7 @@
     attr_id: AttrId,
     /// The `#[derive]` call
     attr_call_id: MacroCallId,
-    derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
+    derive_call_ids: SmallVec<[Option<MacroCallId>; 4]>,
 }
 
 pub(crate) static BUILTIN_SCOPE: LazyLock<FxIndexMap<Name, PerNs>> = LazyLock::new(|| {
@@ -322,7 +322,7 @@
     }
 
     pub fn extern_blocks(&self) -> impl Iterator<Item = ExternBlockId> + '_ {
-        self.extern_blocks.iter().flat_map(|it| it.iter()).copied()
+        self.extern_blocks.iter().copied()
     }
 
     pub fn use_decls(&self) -> impl ExactSizeIterator<Item = UseId> + '_ {
@@ -435,7 +435,7 @@
                 ModuleDefId::TraitId(t) => Some(t),
                 _ => None,
             })
-            .chain(self.unnamed_trait_imports.keys().copied())
+            .chain(self.unnamed_trait_imports.iter().map(|&(t, _)| t))
     }
 
     pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
@@ -476,7 +476,7 @@
     }
 
     pub(crate) fn define_extern_block(&mut self, extern_block: ExternBlockId) {
-        self.extern_blocks.get_or_insert_default().push(extern_block);
+        self.extern_blocks.push(extern_block);
     }
 
     pub(crate) fn define_extern_crate_decl(&mut self, extern_crate: ExternCrateId) {
@@ -564,7 +564,7 @@
 
     // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
     pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
-        self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis)
+        self.unnamed_trait_imports.iter().find(|&&(t, _)| t == tr).map(|(_, trait_)| trait_.vis)
     }
 
     pub(crate) fn push_unnamed_trait(
@@ -573,7 +573,7 @@
         vis: Visibility,
         import: Option<ImportId>,
     ) {
-        self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import });
+        self.unnamed_trait_imports.push((tr, Item { def: (), vis, import }));
     }
 
     pub(crate) fn push_res_with_import(
@@ -720,33 +720,19 @@
     }
 
     /// Marks everything that is not a procedural macro as private to `this_module`.
-    pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
+    pub(crate) fn censor_non_proc_macros(&mut self, krate: Crate) {
         self.types
             .values_mut()
             .map(|def| &mut def.vis)
             .chain(self.values.values_mut().map(|def| &mut def.vis))
-            .chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis))
-            .for_each(|vis| match vis {
-                &mut Visibility::Module(_, visibility_explicitness) => {
-                    *vis = Visibility::Module(this_module, visibility_explicitness)
-                }
-                Visibility::Public => {
-                    *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
-                }
-            });
+            .chain(self.unnamed_trait_imports.iter_mut().map(|(_, def)| &mut def.vis))
+            .for_each(|vis| *vis = Visibility::PubCrate(krate));
 
         for mac in self.macros.values_mut() {
             if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) {
                 continue;
             }
-            match mac.vis {
-                Visibility::Module(_, visibility_explicitness) => {
-                    mac.vis = Visibility::Module(this_module, visibility_explicitness)
-                }
-                Visibility::Public => {
-                    mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
-                }
-            }
+            mac.vis = Visibility::PubCrate(krate)
         }
     }
 
@@ -817,9 +803,7 @@
             macro_invocations,
             extern_blocks,
         } = self;
-        if let Some(it) = extern_blocks {
-            it.shrink_to_fit();
-        }
+        extern_blocks.shrink_to_fit();
         types.shrink_to_fit();
         values.shrink_to_fit();
         macros.shrink_to_fit();
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 1b97eb7..c633339 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -29,7 +29,6 @@
 //!
 //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its
 //! surface syntax.
-#![allow(unexpected_cfgs)]
 
 mod lower;
 mod pretty;
@@ -38,38 +37,40 @@
 
 use std::{
     fmt::{self, Debug},
-    hash::{Hash, Hasher},
-    ops::{Index, Range},
+    hash::Hash,
+    ops::Index,
     sync::OnceLock,
 };
 
 use ast::{AstNode, StructKind};
 use base_db::Crate;
 use hir_expand::{
-    ExpandTo, HirFileId, InFile,
+    ExpandTo, HirFileId,
     attrs::RawAttrs,
     mod_path::{ModPath, PathKind},
     name::Name,
 };
-use intern::{Interned, Symbol};
-use la_arena::{Arena, Idx, RawIdx};
+use intern::Interned;
+use la_arena::{Idx, RawIdx};
 use rustc_hash::FxHashMap;
-use smallvec::SmallVec;
 use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
 use stdx::never;
 use syntax::{SyntaxKind, ast, match_ast};
+use thin_vec::ThinVec;
 use triomphe::Arc;
 
 use crate::{BlockId, Lookup, attr::Attrs, db::DefDatabase};
 
+pub(crate) use crate::item_tree::lower::{lower_use_tree, visibility_from_ast};
+
 #[derive(Copy, Clone, Eq, PartialEq)]
-pub struct RawVisibilityId(u32);
+pub(crate) struct RawVisibilityId(u32);
 
 impl RawVisibilityId {
-    pub const PUB: Self = RawVisibilityId(u32::MAX);
-    pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::MAX - 1);
-    pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::MAX - 2);
-    pub const PUB_CRATE: Self = RawVisibilityId(u32::MAX - 3);
+    const PUB: Self = RawVisibilityId(u32::MAX);
+    const PRIV_IMPLICIT: Self = RawVisibilityId(u32::MAX - 1);
+    const PRIV_EXPLICIT: Self = RawVisibilityId(u32::MAX - 2);
+    const PUB_CRATE: Self = RawVisibilityId(u32::MAX - 3);
 }
 
 impl fmt::Debug for RawVisibilityId {
@@ -85,112 +86,136 @@
     }
 }
 
+#[salsa_macros::tracked(returns(deref))]
+pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
+    let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
+    static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
+
+    let ctx = lower::Ctx::new(db, file_id);
+    let syntax = db.parse_or_expand(file_id);
+    let mut item_tree = match_ast! {
+        match syntax {
+            ast::SourceFile(file) => {
+                let top_attrs = RawAttrs::new(db, &file, ctx.span_map());
+                let mut item_tree = ctx.lower_module_items(&file);
+                item_tree.top_attrs = top_attrs;
+                item_tree
+            },
+            ast::MacroItems(items) => {
+                ctx.lower_module_items(&items)
+            },
+            ast::MacroStmts(stmts) => {
+                // The produced statements can include items, which should be added as top-level
+                // items.
+                ctx.lower_macro_stmts(stmts)
+            },
+            _ => {
+                if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) {
+                    return Default::default();
+                }
+                panic!("cannot create item tree for file {file_id:?} from {syntax:?} {syntax}");
+            },
+        }
+    };
+    let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree;
+    if small_data.is_empty()
+        && big_data.is_empty()
+        && top_level.is_empty()
+        && attrs.is_empty()
+        && top_attrs.is_empty()
+        && vis.arena.is_empty()
+    {
+        EMPTY
+            .get_or_init(|| {
+                Arc::new(ItemTree {
+                    top_level: Box::new([]),
+                    attrs: FxHashMap::default(),
+                    small_data: FxHashMap::default(),
+                    big_data: FxHashMap::default(),
+                    top_attrs: RawAttrs::EMPTY,
+                    vis: ItemVisibilities { arena: ThinVec::new() },
+                })
+            })
+            .clone()
+    } else {
+        item_tree.shrink_to_fit();
+        Arc::new(item_tree)
+    }
+}
+
+#[salsa_macros::tracked(returns(deref))]
+pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
+    let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
+    static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
+
+    let loc = block.lookup(db);
+    let block = loc.ast_id.to_node(db);
+
+    let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
+    let mut item_tree = ctx.lower_block(&block);
+    let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree;
+    if small_data.is_empty()
+        && big_data.is_empty()
+        && top_level.is_empty()
+        && attrs.is_empty()
+        && top_attrs.is_empty()
+        && vis.arena.is_empty()
+    {
+        EMPTY
+            .get_or_init(|| {
+                Arc::new(ItemTree {
+                    top_level: Box::new([]),
+                    attrs: FxHashMap::default(),
+                    small_data: FxHashMap::default(),
+                    big_data: FxHashMap::default(),
+                    top_attrs: RawAttrs::EMPTY,
+                    vis: ItemVisibilities { arena: ThinVec::new() },
+                })
+            })
+            .clone()
+    } else {
+        item_tree.shrink_to_fit();
+        Arc::new(item_tree)
+    }
+}
 /// The item tree of a source file.
 #[derive(Debug, Default, Eq, PartialEq)]
 pub struct ItemTree {
-    top_level: SmallVec<[ModItem; 1]>,
-    attrs: FxHashMap<AttrOwner, RawAttrs>,
-
-    data: Option<Box<ItemTreeData>>,
+    top_level: Box<[ModItemId]>,
+    top_attrs: RawAttrs,
+    attrs: FxHashMap<FileAstId<ast::Item>, RawAttrs>,
+    vis: ItemVisibilities,
+    big_data: FxHashMap<FileAstId<ast::Item>, BigModItem>,
+    small_data: FxHashMap<FileAstId<ast::Item>, SmallModItem>,
 }
 
 impl ItemTree {
-    pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
-        let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
-        static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
-
-        let ctx = lower::Ctx::new(db, file_id);
-        let syntax = db.parse_or_expand(file_id);
-        let mut top_attrs = None;
-        let mut item_tree = match_ast! {
-            match syntax {
-                ast::SourceFile(file) => {
-                    top_attrs = Some(RawAttrs::new(db, &file, ctx.span_map()));
-                    ctx.lower_module_items(&file)
-                },
-                ast::MacroItems(items) => {
-                    ctx.lower_module_items(&items)
-                },
-                ast::MacroStmts(stmts) => {
-                    // The produced statements can include items, which should be added as top-level
-                    // items.
-                    ctx.lower_macro_stmts(stmts)
-                },
-                _ => {
-                    if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) {
-                        return Default::default();
-                    }
-                    panic!("cannot create item tree for file {file_id:?} from {syntax:?} {syntax}");
-                },
-            }
-        };
-
-        if let Some(attrs) = top_attrs {
-            item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
-        }
-        if item_tree.data.is_none() && item_tree.top_level.is_empty() && item_tree.attrs.is_empty()
-        {
-            EMPTY
-                .get_or_init(|| {
-                    Arc::new(ItemTree {
-                        top_level: SmallVec::new_const(),
-                        attrs: FxHashMap::default(),
-                        data: None,
-                    })
-                })
-                .clone()
-        } else {
-            item_tree.shrink_to_fit();
-            Arc::new(item_tree)
-        }
-    }
-
-    pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
-        let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
-        static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
-
-        let loc = block.lookup(db);
-        let block = loc.ast_id.to_node(db);
-
-        let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
-        let mut item_tree = ctx.lower_block(&block);
-        if item_tree.data.is_none() && item_tree.top_level.is_empty() && item_tree.attrs.is_empty()
-        {
-            EMPTY
-                .get_or_init(|| {
-                    Arc::new(ItemTree {
-                        top_level: SmallVec::new_const(),
-                        attrs: FxHashMap::default(),
-                        data: None,
-                    })
-                })
-                .clone()
-        } else {
-            item_tree.shrink_to_fit();
-            Arc::new(item_tree)
-        }
-    }
-
     /// Returns an iterator over all items located at the top level of the `HirFileId` this
     /// `ItemTree` was created from.
-    pub fn top_level_items(&self) -> &[ModItem] {
+    pub(crate) fn top_level_items(&self) -> &[ModItemId] {
         &self.top_level
     }
 
     /// Returns the inner attributes of the source file.
-    pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
-        Attrs::expand_cfg_attr(
-            db,
-            krate,
-            self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&RawAttrs::EMPTY).clone(),
-        )
+    pub(crate) fn top_level_raw_attrs(&self) -> &RawAttrs {
+        &self.top_attrs
     }
 
-    pub(crate) fn raw_attrs(&self, of: AttrOwner) -> &RawAttrs {
+    /// Returns the inner attributes of the source file.
+    pub(crate) fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
+        Attrs::expand_cfg_attr(db, krate, self.top_attrs.clone())
+    }
+
+    pub(crate) fn raw_attrs(&self, of: FileAstId<ast::Item>) -> &RawAttrs {
         self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
     }
 
-    pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: Crate, of: AttrOwner) -> Attrs {
+    pub(crate) fn attrs(
+        &self,
+        db: &dyn DefDatabase,
+        krate: Crate,
+        of: FileAstId<ast::Item>,
+    ) -> Attrs {
         Attrs::expand_cfg_attr(db, krate, self.raw_attrs(of).clone())
     }
 
@@ -198,131 +223,79 @@
     ///
     /// For more detail, see [`ItemTreeDataStats`].
     pub fn item_tree_stats(&self) -> ItemTreeDataStats {
-        match self.data {
-            Some(ref data) => ItemTreeDataStats {
-                traits: data.traits.len(),
-                impls: data.impls.len(),
-                mods: data.mods.len(),
-                macro_calls: data.macro_calls.len(),
-                macro_rules: data.macro_rules.len(),
-            },
-            None => ItemTreeDataStats::default(),
+        let mut traits = 0;
+        let mut impls = 0;
+        let mut mods = 0;
+        let mut macro_calls = 0;
+        let mut macro_rules = 0;
+        for item in self.small_data.values() {
+            match item {
+                SmallModItem::Trait(_) => traits += 1,
+                SmallModItem::Impl(_) => impls += 1,
+                SmallModItem::MacroRules(_) => macro_rules += 1,
+                SmallModItem::MacroCall(_) => macro_calls += 1,
+                _ => {}
+            }
         }
+        for item in self.big_data.values() {
+            match item {
+                BigModItem::Mod(_) => mods += 1,
+                _ => {}
+            }
+        }
+        ItemTreeDataStats { traits, impls, mods, macro_calls, macro_rules }
     }
 
     pub fn pretty_print(&self, db: &dyn DefDatabase, edition: Edition) -> String {
         pretty::print_item_tree(db, self, edition)
     }
 
-    fn data(&self) -> &ItemTreeData {
-        self.data.as_ref().expect("attempted to access data of empty ItemTree")
-    }
-
-    fn data_mut(&mut self) -> &mut ItemTreeData {
-        self.data.get_or_insert_with(Box::default)
-    }
-
     fn shrink_to_fit(&mut self) {
-        let ItemTree { top_level, attrs, data } = self;
-        top_level.shrink_to_fit();
+        let ItemTree { top_level: _, attrs, big_data, small_data, vis: _, top_attrs: _ } = self;
         attrs.shrink_to_fit();
-        if let Some(data) = data {
-            let ItemTreeData {
-                uses,
-                extern_crates,
-                extern_blocks,
-                functions,
-                structs,
-                unions,
-                enums,
-                variants,
-                consts,
-                statics,
-                traits,
-                trait_aliases,
-                impls,
-                type_aliases,
-                mods,
-                macro_calls,
-                macro_rules,
-                macro_defs,
-                vis,
-            } = &mut **data;
-
-            uses.shrink_to_fit();
-            extern_crates.shrink_to_fit();
-            extern_blocks.shrink_to_fit();
-            functions.shrink_to_fit();
-            structs.shrink_to_fit();
-            unions.shrink_to_fit();
-            enums.shrink_to_fit();
-            variants.shrink_to_fit();
-            consts.shrink_to_fit();
-            statics.shrink_to_fit();
-            traits.shrink_to_fit();
-            trait_aliases.shrink_to_fit();
-            impls.shrink_to_fit();
-            type_aliases.shrink_to_fit();
-            mods.shrink_to_fit();
-            macro_calls.shrink_to_fit();
-            macro_rules.shrink_to_fit();
-            macro_defs.shrink_to_fit();
-
-            vis.arena.shrink_to_fit();
-        }
+        big_data.shrink_to_fit();
+        small_data.shrink_to_fit();
     }
 }
 
 #[derive(Default, Debug, Eq, PartialEq)]
 struct ItemVisibilities {
-    arena: Arena<RawVisibility>,
+    arena: ThinVec<RawVisibility>,
 }
 
-impl ItemVisibilities {
-    fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
-        match &vis {
-            RawVisibility::Public => RawVisibilityId::PUB,
-            RawVisibility::Module(path, explicitiy) if path.segments().is_empty() => {
-                match (path.kind, explicitiy) {
-                    (PathKind::SELF, VisibilityExplicitness::Explicit) => {
-                        RawVisibilityId::PRIV_EXPLICIT
-                    }
-                    (PathKind::SELF, VisibilityExplicitness::Implicit) => {
-                        RawVisibilityId::PRIV_IMPLICIT
-                    }
-                    (PathKind::Crate, _) => RawVisibilityId::PUB_CRATE,
-                    _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
-                }
-            }
-            _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
-        }
-    }
+#[derive(Debug, Clone, Eq, PartialEq)]
+enum SmallModItem {
+    Const(Const),
+    Enum(Enum),
+    ExternBlock(ExternBlock),
+    Function(Function),
+    Impl(Impl),
+    Macro2(Macro2),
+    MacroCall(MacroCall),
+    MacroRules(MacroRules),
+    Static(Static),
+    Struct(Struct),
+    Trait(Trait),
+    TraitAlias(TraitAlias),
+    TypeAlias(TypeAlias),
+    Union(Union),
 }
 
-#[derive(Default, Debug, Eq, PartialEq)]
-struct ItemTreeData {
-    uses: Arena<Use>,
-    extern_crates: Arena<ExternCrate>,
-    extern_blocks: Arena<ExternBlock>,
-    functions: Arena<Function>,
-    structs: Arena<Struct>,
-    unions: Arena<Union>,
-    enums: Arena<Enum>,
-    variants: Arena<Variant>,
-    consts: Arena<Const>,
-    statics: Arena<Static>,
-    traits: Arena<Trait>,
-    trait_aliases: Arena<TraitAlias>,
-    impls: Arena<Impl>,
-    type_aliases: Arena<TypeAlias>,
-    mods: Arena<Mod>,
-    macro_calls: Arena<MacroCall>,
-    macro_rules: Arena<MacroRules>,
-    macro_defs: Arena<Macro2>,
-
-    vis: ItemVisibilities,
+#[derive(Debug, Clone, Eq, PartialEq)]
+enum BigModItem {
+    ExternCrate(ExternCrate),
+    Mod(Mod),
+    Use(Use),
 }
 
+// `ModItem` is stored a bunch in `ItemTree`'s so we pay the max for each item. It should stay as
+// small as possible which is why we split them in two, most common ones are 3 usize but some rarer
+// ones are 5.
+#[cfg(target_pointer_width = "64")]
+const _: [(); std::mem::size_of::<BigModItem>()] = [(); std::mem::size_of::<[usize; 5]>()];
+#[cfg(target_pointer_width = "64")]
+const _: [(); std::mem::size_of::<SmallModItem>()] = [(); std::mem::size_of::<[usize; 3]>()];
+
 #[derive(Default, Debug, Eq, PartialEq)]
 pub struct ItemTreeDataStats {
     pub traits: usize,
@@ -332,100 +305,13 @@
     pub macro_rules: usize,
 }
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
-pub enum AttrOwner {
-    /// Attributes on an item.
-    ModItem(ModItem),
-    /// Inner attributes of the source file.
-    TopLevel,
-
-    Variant(FileItemTreeId<Variant>),
-    // while not relevant to early name resolution, fields can contain visibility
-    Field(FieldParent, ItemTreeFieldId),
-}
-
-impl AttrOwner {
-    pub fn make_field_indexed(parent: FieldParent, idx: usize) -> Self {
-        AttrOwner::Field(parent, ItemTreeFieldId::from_raw(RawIdx::from_u32(idx as u32)))
-    }
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum FieldParent {
-    Struct(FileItemTreeId<Struct>),
-    Union(FileItemTreeId<Union>),
-    EnumVariant(FileItemTreeId<Variant>),
-}
-
-pub type ItemTreeFieldId = Idx<Field>;
-
-macro_rules! from_attrs {
-    ( $( $var:ident($t:ty) ),+ $(,)? ) => {
-        $(
-            impl From<$t> for AttrOwner {
-                fn from(t: $t) -> AttrOwner {
-                    AttrOwner::$var(t)
-                }
-            }
-        )+
-    };
-}
-
-from_attrs!(ModItem(ModItem), Variant(FileItemTreeId<Variant>));
-
 /// Trait implemented by all nodes in the item tree.
-pub trait ItemTreeNode: Clone {
+pub(crate) trait ItemTreeNode: Clone {
     type Source: AstIdNode;
-
-    fn ast_id(&self) -> FileAstId<Self::Source>;
-
-    /// Looks up an instance of `Self` in an item tree.
-    fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
-    fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner;
 }
 
-pub struct FileItemTreeId<N>(Idx<N>);
-
-impl<N> FileItemTreeId<N> {
-    pub fn range_iter(range: Range<Self>) -> impl Iterator<Item = Self> + Clone {
-        (range.start.index().into_raw().into_u32()..range.end.index().into_raw().into_u32())
-            .map(RawIdx::from_u32)
-            .map(Idx::from_raw)
-            .map(Self)
-    }
-}
-
-impl<N> FileItemTreeId<N> {
-    pub fn index(&self) -> Idx<N> {
-        self.0
-    }
-}
-
-impl<N> Clone for FileItemTreeId<N> {
-    fn clone(&self) -> Self {
-        *self
-    }
-}
-impl<N> Copy for FileItemTreeId<N> {}
-
-impl<N> PartialEq for FileItemTreeId<N> {
-    fn eq(&self, other: &FileItemTreeId<N>) -> bool {
-        self.0 == other.0
-    }
-}
-impl<N> Eq for FileItemTreeId<N> {}
-
-impl<N> Hash for FileItemTreeId<N> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.0.hash(state)
-    }
-}
-
-impl<N> fmt::Debug for FileItemTreeId<N> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
+#[allow(type_alias_bounds)]
+pub(crate) type ItemTreeAstId<T: ItemTreeNode> = FileAstId<T::Source>;
 
 /// Identifies a particular [`ItemTree`].
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
@@ -435,100 +321,48 @@
 }
 
 impl TreeId {
-    pub fn new(file: HirFileId, block: Option<BlockId>) -> Self {
+    pub(crate) fn new(file: HirFileId, block: Option<BlockId>) -> Self {
         Self { file, block }
     }
 
-    pub fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
+    pub(crate) fn item_tree<'db>(&self, db: &'db dyn DefDatabase) -> &'db ItemTree {
         match self.block {
-            Some(block) => db.block_item_tree(block),
-            None => db.file_item_tree(self.file),
+            Some(block) => block_item_tree_query(db, block),
+            None => file_item_tree_query(db, self.file),
         }
     }
 
+    #[inline]
     pub fn file_id(self) -> HirFileId {
         self.file
     }
 
-    pub fn is_block(self) -> bool {
+    pub(crate) fn is_block(self) -> bool {
         self.block.is_some()
     }
 }
 
-#[derive(Debug)]
-pub struct ItemTreeId<N> {
-    tree: TreeId,
-    pub value: FileItemTreeId<N>,
-}
-
-impl<N> ItemTreeId<N> {
-    pub fn new(tree: TreeId, idx: FileItemTreeId<N>) -> Self {
-        Self { tree, value: idx }
-    }
-
-    pub fn file_id(self) -> HirFileId {
-        self.tree.file
-    }
-
-    pub fn tree_id(self) -> TreeId {
-        self.tree
-    }
-
-    pub fn item_tree(self, db: &dyn DefDatabase) -> Arc<ItemTree> {
-        self.tree.item_tree(db)
-    }
-
-    pub fn resolved<R>(self, db: &dyn DefDatabase, cb: impl FnOnce(&N) -> R) -> R
-    where
-        ItemTree: Index<FileItemTreeId<N>, Output = N>,
-    {
-        cb(&self.tree.item_tree(db)[self.value])
-    }
-}
-
-impl<N> Copy for ItemTreeId<N> {}
-impl<N> Clone for ItemTreeId<N> {
-    fn clone(&self) -> Self {
-        *self
-    }
-}
-
-impl<N> PartialEq for ItemTreeId<N> {
-    fn eq(&self, other: &Self) -> bool {
-        self.tree == other.tree && self.value == other.value
-    }
-}
-
-impl<N> Eq for ItemTreeId<N> {}
-
-impl<N> Hash for ItemTreeId<N> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.tree.hash(state);
-        self.value.hash(state);
-    }
-}
-
 macro_rules! mod_items {
-    ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
+    ($mod_item:ident -> $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
         #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-        pub enum ModItem {
+        pub(crate) enum $mod_item {
             $(
-                $typ(FileItemTreeId<$typ>),
+                $typ(FileAstId<$ast>),
             )+
         }
 
-        impl ModItem {
-            pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
+        impl $mod_item {
+            pub(crate) fn ast_id(self) -> FileAstId<ast::Item> {
                 match self {
-                    $(ModItem::$typ(it) => tree[it.index()].ast_id().upcast()),+
+                    $($mod_item::$typ(it) => it.upcast()),+
                 }
             }
         }
 
         $(
-            impl From<FileItemTreeId<$typ>> for ModItem {
-                fn from(id: FileItemTreeId<$typ>) -> ModItem {
-                    ModItem::$typ(id)
+            impl From<FileAstId<$ast>> for $mod_item {
+                fn from(id: FileAstId<$ast>) -> $mod_item {
+                    ModItemId::$typ(id)
                 }
             }
         )+
@@ -536,25 +370,19 @@
         $(
             impl ItemTreeNode for $typ {
                 type Source = $ast;
-
-                fn ast_id(&self) -> FileAstId<Self::Source> {
-                    self.ast_id
-                }
-
-                fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
-                    &tree.data().$fld[index]
-                }
-
-                fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner {
-                    AttrOwner::ModItem(ModItem::$typ(id))
-                }
             }
 
-            impl Index<Idx<$typ>> for ItemTree {
+            impl Index<FileAstId<$ast>> for ItemTree {
                 type Output = $typ;
 
-                fn index(&self, index: Idx<$typ>) -> &Self::Output {
-                    &self.data().$fld[index]
+                #[allow(unused_imports)]
+                fn index(&self, index: FileAstId<$ast>) -> &Self::Output {
+                    use BigModItem::*;
+                    use SmallModItem::*;
+                    match &self.$fld[&index.upcast()] {
+                        $typ(item) => item,
+                        _ => panic!("expected item of type `{}` at index `{:?}`", stringify!($typ), index),
+                    }
                 }
             }
         )+
@@ -562,94 +390,59 @@
 }
 
 mod_items! {
-    Use in uses -> ast::Use,
-    ExternCrate in extern_crates -> ast::ExternCrate,
-    ExternBlock in extern_blocks -> ast::ExternBlock,
-    Function in functions -> ast::Fn,
-    Struct in structs -> ast::Struct,
-    Union in unions -> ast::Union,
-    Enum in enums -> ast::Enum,
-    Const in consts -> ast::Const,
-    Static in statics -> ast::Static,
-    Trait in traits -> ast::Trait,
-    TraitAlias in trait_aliases -> ast::TraitAlias,
-    Impl in impls -> ast::Impl,
-    TypeAlias in type_aliases -> ast::TypeAlias,
-    Mod in mods -> ast::Module,
-    MacroCall in macro_calls -> ast::MacroCall,
-    MacroRules in macro_rules -> ast::MacroRules,
-    Macro2 in macro_defs -> ast::MacroDef,
+ModItemId ->
+    Const in small_data -> ast::Const,
+    Enum in small_data -> ast::Enum,
+    ExternBlock in small_data -> ast::ExternBlock,
+    ExternCrate in big_data -> ast::ExternCrate,
+    Function in small_data -> ast::Fn,
+    Impl in small_data -> ast::Impl,
+    Macro2 in small_data -> ast::MacroDef,
+    MacroCall in small_data -> ast::MacroCall,
+    MacroRules in small_data -> ast::MacroRules,
+    Mod in big_data -> ast::Module,
+    Static in small_data -> ast::Static,
+    Struct in small_data -> ast::Struct,
+    Trait in small_data -> ast::Trait,
+    TraitAlias in small_data -> ast::TraitAlias,
+    TypeAlias in small_data -> ast::TypeAlias,
+    Union in small_data -> ast::Union,
+    Use in big_data -> ast::Use,
 }
 
 impl Index<RawVisibilityId> for ItemTree {
     type Output = RawVisibility;
     fn index(&self, index: RawVisibilityId) -> &Self::Output {
         static VIS_PUB: RawVisibility = RawVisibility::Public;
-        static VIS_PRIV_IMPLICIT: OnceLock<RawVisibility> = OnceLock::new();
-        static VIS_PRIV_EXPLICIT: OnceLock<RawVisibility> = OnceLock::new();
-        static VIS_PUB_CRATE: OnceLock<RawVisibility> = OnceLock::new();
+        static VIS_PRIV_IMPLICIT: RawVisibility =
+            RawVisibility::PubSelf(VisibilityExplicitness::Implicit);
+        static VIS_PRIV_EXPLICIT: RawVisibility =
+            RawVisibility::PubSelf(VisibilityExplicitness::Explicit);
+        static VIS_PUB_CRATE: RawVisibility = RawVisibility::PubCrate;
 
         match index {
-            RawVisibilityId::PRIV_IMPLICIT => VIS_PRIV_IMPLICIT.get_or_init(|| {
-                RawVisibility::Module(
-                    Interned::new(ModPath::from_kind(PathKind::SELF)),
-                    VisibilityExplicitness::Implicit,
-                )
-            }),
-            RawVisibilityId::PRIV_EXPLICIT => VIS_PRIV_EXPLICIT.get_or_init(|| {
-                RawVisibility::Module(
-                    Interned::new(ModPath::from_kind(PathKind::SELF)),
-                    VisibilityExplicitness::Explicit,
-                )
-            }),
+            RawVisibilityId::PRIV_IMPLICIT => &VIS_PRIV_IMPLICIT,
+            RawVisibilityId::PRIV_EXPLICIT => &VIS_PRIV_EXPLICIT,
             RawVisibilityId::PUB => &VIS_PUB,
-            RawVisibilityId::PUB_CRATE => VIS_PUB_CRATE.get_or_init(|| {
-                RawVisibility::Module(
-                    Interned::new(ModPath::from_kind(PathKind::Crate)),
-                    VisibilityExplicitness::Explicit,
-                )
-            }),
-            _ => &self.data().vis.arena[Idx::from_raw(index.0.into())],
+            RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
+            _ => &self.vis.arena[index.0 as usize],
         }
     }
 }
 
-impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
-    type Output = N;
-    fn index(&self, id: FileItemTreeId<N>) -> &N {
-        N::lookup(self, id.index())
-    }
-}
-
-impl ItemTreeNode for Variant {
-    type Source = ast::Variant;
-
-    fn ast_id(&self) -> FileAstId<Self::Source> {
-        self.ast_id
-    }
-
-    fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
-        &tree.data().variants[index]
-    }
-
-    fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner {
-        AttrOwner::Variant(id)
-    }
-}
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Use {
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::Use>,
-    pub use_tree: UseTree,
+    pub(crate) visibility: RawVisibilityId,
+    pub(crate) use_tree: UseTree,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct UseTree {
-    pub index: Idx<ast::UseTree>,
     kind: UseTreeKind,
 }
 
+// FIXME: Would be nice to encode `None` into this
+// We could just use a `Name` where `_` well means `_` ..
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ImportAlias {
     /// Unnamed alias, as in `use Foo as _;`
@@ -703,55 +496,37 @@
 pub struct ExternCrate {
     pub name: Name,
     pub alias: Option<ImportAlias>,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::ExternCrate>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct ExternBlock {
-    pub abi: Option<Symbol>,
-    pub ast_id: FileAstId<ast::ExternBlock>,
-    pub children: Box<[ModItem]>,
+    pub(crate) children: Box<[ModItemId]>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Function {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::Fn>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Struct {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub fields: Box<[Field]>,
+    pub(crate) visibility: RawVisibilityId,
     pub shape: FieldsShape,
-    pub ast_id: FileAstId<ast::Struct>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Union {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub fields: Box<[Field]>,
-    pub ast_id: FileAstId<ast::Union>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Enum {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub variants: Range<FileItemTreeId<Variant>>,
-    pub ast_id: FileAstId<ast::Enum>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Variant {
-    pub name: Name,
-    pub fields: Box<[Field]>,
-    pub shape: FieldsShape,
-    pub ast_id: FileAstId<ast::Variant>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -762,11 +537,15 @@
 }
 
 /// Visibility of an item, not yet resolved.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum RawVisibility {
     /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
     /// equivalent to `pub(self)`.
     Module(Interned<ModPath>, VisibilityExplicitness),
+    /// `pub(self)`.
+    PubSelf(VisibilityExplicitness),
+    /// `pub(crate)`.
+    PubCrate,
     /// `pub`.
     Public,
 }
@@ -785,71 +564,51 @@
     }
 }
 
-// FIXME: Remove this from item tree?
-/// A single field of an enum variant or struct
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Field {
-    pub name: Name,
-    pub visibility: RawVisibilityId,
-    // FIXME: Not an item tree property
-    pub is_unsafe: bool,
-}
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Const {
     /// `None` for `const _: () = ();`
     pub name: Option<Name>,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::Const>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Static {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::Static>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Trait {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub items: Box<[AssocItem]>,
-    pub ast_id: FileAstId<ast::Trait>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct TraitAlias {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::TraitAlias>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct Impl {
-    pub items: Box<[AssocItem]>,
-    pub ast_id: FileAstId<ast::Impl>,
-}
+pub struct Impl {}
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TypeAlias {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::TypeAlias>,
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Mod {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub kind: ModKind,
-    pub ast_id: FileAstId<ast::Module>,
+    pub(crate) visibility: RawVisibilityId,
+    pub(crate) kind: ModKind,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub enum ModKind {
+pub(crate) enum ModKind {
     /// `mod m { ... }`
-    Inline { items: Box<[ModItem]> },
+    Inline { items: Box<[ModItemId]> },
     /// `mod m;`
     Outline,
 }
@@ -858,7 +617,6 @@
 pub struct MacroCall {
     /// Path to the called macro.
     pub path: Interned<ModPath>,
-    pub ast_id: FileAstId<ast::MacroCall>,
     pub expand_to: ExpandTo,
     pub ctxt: SyntaxContext,
 }
@@ -867,52 +625,13 @@
 pub struct MacroRules {
     /// The name of the declared macro.
     pub name: Name,
-    pub ast_id: FileAstId<ast::MacroRules>,
 }
 
 /// "Macros 2.0" macro definition.
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Macro2 {
     pub name: Name,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FileAstId<ast::MacroDef>,
-}
-
-impl Use {
-    /// Maps a `UseTree` contained in this import back to its AST node.
-    pub fn use_tree_to_ast(
-        &self,
-        db: &dyn DefDatabase,
-        file_id: HirFileId,
-        index: Idx<ast::UseTree>,
-    ) -> ast::UseTree {
-        // Re-lower the AST item and get the source map.
-        // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
-        let ast = InFile::new(file_id, self.ast_id).to_node(db);
-        let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
-        let (_, source_map) = lower::lower_use_tree(db, ast_use_tree, &mut |range| {
-            db.span_map(file_id).span_for_range(range).ctx
-        })
-        .expect("failed to lower use tree");
-        source_map[index].clone()
-    }
-
-    /// Maps a `UseTree` contained in this import back to its AST node.
-    pub fn use_tree_source_map(
-        &self,
-        db: &dyn DefDatabase,
-        file_id: HirFileId,
-    ) -> Arena<ast::UseTree> {
-        // Re-lower the AST item and get the source map.
-        // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
-        let ast = InFile::new(file_id, self.ast_id).to_node(db);
-        let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
-        lower::lower_use_tree(db, ast_use_tree, &mut |range| {
-            db.span_map(file_id).span_for_range(range).ctx
-        })
-        .expect("failed to lower use tree")
-        .1
-    }
+    pub(crate) visibility: RawVisibilityId,
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -925,15 +644,17 @@
     TypeOnly,
 }
 
-impl UseTree {
+impl Use {
     /// Expands the `UseTree` into individually imported `ModPath`s.
     pub fn expand(
         &self,
         mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
     ) {
-        self.expand_impl(None, &mut cb)
+        self.use_tree.expand_impl(None, &mut 0, &mut cb)
     }
+}
 
+impl UseTree {
     /// The [`UseTreeKind`] of this `UseTree`.
     pub fn kind(&self) -> &UseTreeKind {
         &self.kind
@@ -942,6 +663,7 @@
     fn expand_impl(
         &self,
         prefix: Option<ModPath>,
+        counting_index: &mut u32,
         cb: &mut impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
     ) {
         fn concat_mod_paths(
@@ -977,17 +699,27 @@
         match &self.kind {
             UseTreeKind::Single { path, alias } => {
                 if let Some((path, kind)) = concat_mod_paths(prefix, path) {
-                    cb(self.index, path, kind, alias.clone());
+                    cb(Idx::from_raw(RawIdx::from_u32(*counting_index)), path, kind, alias.clone());
                 }
             }
             UseTreeKind::Glob { path: Some(path) } => {
                 if let Some((path, _)) = concat_mod_paths(prefix, path) {
-                    cb(self.index, path, ImportKind::Glob, None);
+                    cb(
+                        Idx::from_raw(RawIdx::from_u32(*counting_index)),
+                        path,
+                        ImportKind::Glob,
+                        None,
+                    );
                 }
             }
             UseTreeKind::Glob { path: None } => {
                 if let Some(prefix) = prefix {
-                    cb(self.index, prefix, ImportKind::Glob, None);
+                    cb(
+                        Idx::from_raw(RawIdx::from_u32(*counting_index)),
+                        prefix,
+                        ImportKind::Glob,
+                        None,
+                    );
                 }
             }
             UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
@@ -999,82 +731,10 @@
                     None => prefix,
                 };
                 for tree in &**list {
-                    tree.expand_impl(prefix.clone(), cb);
+                    *counting_index += 1;
+                    tree.expand_impl(prefix.clone(), counting_index, cb);
                 }
             }
         }
     }
 }
-
-macro_rules! impl_froms {
-    ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
-        $(
-            impl From<$t> for $e {
-                fn from(it: $t) -> $e {
-                    $e::$v(it)
-                }
-            }
-        )*
-    }
-}
-
-impl ModItem {
-    pub fn as_assoc_item(&self) -> Option<AssocItem> {
-        match self {
-            ModItem::Use(_)
-            | ModItem::ExternCrate(_)
-            | ModItem::ExternBlock(_)
-            | ModItem::Struct(_)
-            | ModItem::Union(_)
-            | ModItem::Enum(_)
-            | ModItem::Static(_)
-            | ModItem::Trait(_)
-            | ModItem::TraitAlias(_)
-            | ModItem::Impl(_)
-            | ModItem::Mod(_)
-            | ModItem::MacroRules(_)
-            | ModItem::Macro2(_) => None,
-            &ModItem::MacroCall(call) => Some(AssocItem::MacroCall(call)),
-            &ModItem::Const(konst) => Some(AssocItem::Const(konst)),
-            &ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(alias)),
-            &ModItem::Function(func) => Some(AssocItem::Function(func)),
-        }
-    }
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum AssocItem {
-    Function(FileItemTreeId<Function>),
-    TypeAlias(FileItemTreeId<TypeAlias>),
-    Const(FileItemTreeId<Const>),
-    MacroCall(FileItemTreeId<MacroCall>),
-}
-
-impl_froms!(AssocItem {
-    Function(FileItemTreeId<Function>),
-    TypeAlias(FileItemTreeId<TypeAlias>),
-    Const(FileItemTreeId<Const>),
-    MacroCall(FileItemTreeId<MacroCall>),
-});
-
-impl From<AssocItem> for ModItem {
-    fn from(item: AssocItem) -> Self {
-        match item {
-            AssocItem::Function(it) => it.into(),
-            AssocItem::TypeAlias(it) => it.into(),
-            AssocItem::Const(it) => it.into(),
-            AssocItem::MacroCall(it) => it.into(),
-        }
-    }
-}
-
-impl AssocItem {
-    pub fn ast_id(self, tree: &ItemTree) -> FileAstId<ast::AssocItem> {
-        match self {
-            AssocItem::Function(id) => tree[id].ast_id.upcast(),
-            AssocItem::TypeAlias(id) => tree[id].ast_id.upcast(),
-            AssocItem::Const(id) => tree[id].ast_id.upcast(),
-            AssocItem::MacroCall(id) => tree[id].ast_id.upcast(),
-        }
-    }
-}
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index b490e16..f327366 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -2,42 +2,40 @@
 
 use std::{cell::OnceCell, collections::hash_map::Entry};
 
+use base_db::FxIndexSet;
 use hir_expand::{
     HirFileId,
     mod_path::PathKind,
     name::AsName,
     span_map::{SpanMap, SpanMapRef},
 };
-use intern::{Symbol, sym};
 use la_arena::Arena;
-use span::{AstIdMap, SyntaxContext};
+use span::{AstIdMap, FileAstId, SyntaxContext};
 use syntax::{
     AstNode,
-    ast::{self, HasModuleItem, HasName, IsString},
+    ast::{self, HasModuleItem, HasName},
 };
 use triomphe::Arc;
 
 use crate::{
     db::DefDatabase,
     item_tree::{
-        AssocItem, AttrOwner, Const, Enum, ExternBlock, ExternCrate, Field, FieldParent,
-        FieldsShape, FileItemTreeId, Function, Idx, Impl, ImportAlias, Interned, ItemTree,
-        ItemTreeData, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, ModPath, Name, Range,
-        RawAttrs, RawIdx, RawVisibility, RawVisibilityId, Static, Struct, StructKind, Trait,
-        TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, Variant, VisibilityExplicitness,
+        BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl,
+        ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod,
+        ModItemId, ModKind, ModPath, RawAttrs, RawVisibility, RawVisibilityId, SmallModItem,
+        Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind,
+        VisibilityExplicitness,
     },
 };
 
-fn id<N>(index: Idx<N>) -> FileItemTreeId<N> {
-    FileItemTreeId(index)
-}
-
 pub(super) struct Ctx<'a> {
     db: &'a dyn DefDatabase,
     tree: ItemTree,
     source_ast_id_map: Arc<AstIdMap>,
     span_map: OnceCell<SpanMap>,
     file: HirFileId,
+    top_level: Vec<ModItemId>,
+    visibilities: FxIndexSet<RawVisibility>,
 }
 
 impl<'a> Ctx<'a> {
@@ -48,6 +46,8 @@
             source_ast_id_map: db.ast_id_map(file),
             file,
             span_map: OnceCell::new(),
+            visibilities: FxIndexSet::default(),
+            top_level: Vec::new(),
         }
     }
 
@@ -56,13 +56,14 @@
     }
 
     pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
-        self.tree.top_level =
-            item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
+        self.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
+        self.tree.vis.arena = self.visibilities.into_iter().collect();
+        self.tree.top_level = self.top_level.into_boxed_slice();
         self.tree
     }
 
     pub(super) fn lower_macro_stmts(mut self, stmts: ast::MacroStmts) -> ItemTree {
-        self.tree.top_level = stmts
+        self.top_level = stmts
             .statements()
             .filter_map(|stmt| {
                 match stmt {
@@ -86,17 +87,19 @@
             if let Some(call) = tail_macro.macro_call() {
                 cov_mark::hit!(macro_stmt_with_trailing_macro_expr);
                 if let Some(mod_item) = self.lower_mod_item(&call.into()) {
-                    self.tree.top_level.push(mod_item);
+                    self.top_level.push(mod_item);
                 }
             }
         }
 
+        self.tree.vis.arena = self.visibilities.into_iter().collect();
+        self.tree.top_level = self.top_level.into_boxed_slice();
         self.tree
     }
 
     pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
-        self.tree.attrs.insert(AttrOwner::TopLevel, RawAttrs::new(self.db, block, self.span_map()));
-        self.tree.top_level = block
+        self.tree.top_attrs = RawAttrs::new(self.db, block, self.span_map());
+        self.top_level = block
             .statements()
             .filter_map(|stmt| match stmt {
                 ast::Stmt::Item(item) => self.lower_mod_item(&item),
@@ -112,20 +115,17 @@
         if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() {
             if let Some(call) = expr.macro_call() {
                 if let Some(mod_item) = self.lower_mod_item(&call.into()) {
-                    self.tree.top_level.push(mod_item);
+                    self.top_level.push(mod_item);
                 }
             }
         }
-
+        self.tree.vis.arena = self.visibilities.into_iter().collect();
+        self.tree.top_level = self.top_level.into_boxed_slice();
         self.tree
     }
 
-    fn data(&mut self) -> &mut ItemTreeData {
-        self.tree.data_mut()
-    }
-
-    fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItem> {
-        let mod_item: ModItem = match item {
+    fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItemId> {
+        let mod_item: ModItemId = match item {
             ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
             ast::Item::Union(ast) => self.lower_union(ast)?.into(),
             ast::Item::Enum(ast) => self.lower_enum(ast)?.into(),
@@ -145,12 +145,12 @@
             ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
         };
         let attrs = RawAttrs::new(self.db, item, self.span_map());
-        self.add_attrs(mod_item.into(), attrs);
+        self.add_attrs(mod_item.ast_id(), attrs);
 
         Some(mod_item)
     }
 
-    fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) {
+    fn add_attrs(&mut self, item: FileAstId<ast::Item>, attrs: RawAttrs) {
         if !attrs.is_empty() {
             match self.tree.attrs.entry(item) {
                 Entry::Occupied(mut entry) => {
@@ -163,208 +163,78 @@
         }
     }
 
-    fn lower_assoc_item(&mut self, item_node: &ast::AssocItem) -> Option<AssocItem> {
-        let item: AssocItem = match item_node {
-            ast::AssocItem::Fn(ast) => self.lower_function(ast).map(Into::into),
-            ast::AssocItem::TypeAlias(ast) => self.lower_type_alias(ast).map(Into::into),
-            ast::AssocItem::Const(ast) => Some(self.lower_const(ast).into()),
-            ast::AssocItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
-        }?;
-        let attrs = RawAttrs::new(self.db, item_node, self.span_map());
-        self.add_attrs(
-            match item {
-                AssocItem::Function(it) => AttrOwner::ModItem(ModItem::Function(it)),
-                AssocItem::TypeAlias(it) => AttrOwner::ModItem(ModItem::TypeAlias(it)),
-                AssocItem::Const(it) => AttrOwner::ModItem(ModItem::Const(it)),
-                AssocItem::MacroCall(it) => AttrOwner::ModItem(ModItem::MacroCall(it)),
-            },
-            attrs,
-        );
-        Some(item)
-    }
-
-    fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> {
+    fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<ItemTreeAstId<Struct>> {
         let visibility = self.lower_visibility(strukt);
         let name = strukt.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(strukt);
-        let (fields, kind, attrs) = self.lower_fields(&strukt.kind());
-        let res = Struct { name, visibility, fields, shape: kind, ast_id };
-        let id = id(self.data().structs.alloc(res));
+        let shape = adt_shape(strukt.kind());
+        let res = Struct { name, visibility, shape };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Struct(res));
 
-        for (idx, attr) in attrs {
-            self.add_attrs(
-                AttrOwner::Field(
-                    FieldParent::Struct(id),
-                    Idx::from_raw(RawIdx::from_u32(idx as u32)),
-                ),
-                attr,
-            );
-        }
-        Some(id)
+        Some(ast_id)
     }
 
-    fn lower_fields(
-        &mut self,
-        strukt_kind: &ast::StructKind,
-    ) -> (Box<[Field]>, FieldsShape, Vec<(usize, RawAttrs)>) {
-        match strukt_kind {
-            ast::StructKind::Record(it) => {
-                let mut fields = vec![];
-                let mut attrs = vec![];
-
-                for (i, field) in it.fields().enumerate() {
-                    let data = self.lower_record_field(&field);
-                    fields.push(data);
-                    let attr = RawAttrs::new(self.db, &field, self.span_map());
-                    if !attr.is_empty() {
-                        attrs.push((i, attr))
-                    }
-                }
-                (fields.into(), FieldsShape::Record, attrs)
-            }
-            ast::StructKind::Tuple(it) => {
-                let mut fields = vec![];
-                let mut attrs = vec![];
-
-                for (i, field) in it.fields().enumerate() {
-                    let data = self.lower_tuple_field(i, &field);
-                    fields.push(data);
-                    let attr = RawAttrs::new(self.db, &field, self.span_map());
-                    if !attr.is_empty() {
-                        attrs.push((i, attr))
-                    }
-                }
-                (fields.into(), FieldsShape::Tuple, attrs)
-            }
-            ast::StructKind::Unit => (Box::default(), FieldsShape::Unit, Vec::default()),
-        }
-    }
-
-    fn lower_record_field(&mut self, field: &ast::RecordField) -> Field {
-        let name = match field.name() {
-            Some(name) => name.as_name(),
-            None => Name::missing(),
-        };
-        let visibility = self.lower_visibility(field);
-
-        Field { name, visibility, is_unsafe: field.unsafe_token().is_some() }
-    }
-
-    fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleField) -> Field {
-        let name = Name::new_tuple_field(idx);
-        let visibility = self.lower_visibility(field);
-        Field { name, visibility, is_unsafe: false }
-    }
-
-    fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
+    fn lower_union(&mut self, union: &ast::Union) -> Option<ItemTreeAstId<Union>> {
         let visibility = self.lower_visibility(union);
         let name = union.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(union);
-        let (fields, _, attrs) = match union.record_field_list() {
-            Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
-            None => (Box::default(), FieldsShape::Record, Vec::default()),
-        };
-        let res = Union { name, visibility, fields, ast_id };
-        let id = id(self.data().unions.alloc(res));
-        for (idx, attr) in attrs {
-            self.add_attrs(
-                AttrOwner::Field(
-                    FieldParent::Union(id),
-                    Idx::from_raw(RawIdx::from_u32(idx as u32)),
-                ),
-                attr,
-            );
-        }
-        Some(id)
+        let res = Union { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Union(res));
+        Some(ast_id)
     }
 
-    fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> {
+    fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<ItemTreeAstId<Enum>> {
         let visibility = self.lower_visibility(enum_);
         let name = enum_.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(enum_);
-        let variants = match &enum_.variant_list() {
-            Some(variant_list) => self.lower_variants(variant_list),
-            None => {
-                FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx())
-            }
-        };
-        let res = Enum { name, visibility, variants, ast_id };
-        let id = id(self.data().enums.alloc(res));
-        Some(id)
+        let res = Enum { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Enum(res));
+        Some(ast_id)
     }
 
-    fn lower_variants(&mut self, variants: &ast::VariantList) -> Range<FileItemTreeId<Variant>> {
-        let start = self.next_variant_idx();
-        for variant in variants.variants() {
-            let idx = self.lower_variant(&variant);
-            self.add_attrs(id(idx).into(), RawAttrs::new(self.db, &variant, self.span_map()));
-        }
-        let end = self.next_variant_idx();
-        FileItemTreeId(start)..FileItemTreeId(end)
-    }
-
-    fn lower_variant(&mut self, variant: &ast::Variant) -> Idx<Variant> {
-        let name = match variant.name() {
-            Some(name) => name.as_name(),
-            None => Name::missing(),
-        };
-        let (fields, kind, attrs) = self.lower_fields(&variant.kind());
-        let ast_id = self.source_ast_id_map.ast_id(variant);
-        let res = Variant { name, fields, shape: kind, ast_id };
-        let id = self.data().variants.alloc(res);
-        for (idx, attr) in attrs {
-            self.add_attrs(
-                AttrOwner::Field(
-                    FieldParent::EnumVariant(FileItemTreeId(id)),
-                    Idx::from_raw(RawIdx::from_u32(idx as u32)),
-                ),
-                attr,
-            );
-        }
-        id
-    }
-
-    fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> {
+    fn lower_function(&mut self, func: &ast::Fn) -> Option<ItemTreeAstId<Function>> {
         let visibility = self.lower_visibility(func);
         let name = func.name()?.as_name();
 
         let ast_id = self.source_ast_id_map.ast_id(func);
 
-        let res = Function { name, visibility, ast_id };
+        let res = Function { name, visibility };
 
-        let id = id(self.data().functions.alloc(res));
-        Some(id)
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Function(res));
+        Some(ast_id)
     }
 
     fn lower_type_alias(
         &mut self,
         type_alias: &ast::TypeAlias,
-    ) -> Option<FileItemTreeId<TypeAlias>> {
+    ) -> Option<ItemTreeAstId<TypeAlias>> {
         let name = type_alias.name()?.as_name();
         let visibility = self.lower_visibility(type_alias);
         let ast_id = self.source_ast_id_map.ast_id(type_alias);
-        let res = TypeAlias { name, visibility, ast_id };
-        let id = id(self.data().type_aliases.alloc(res));
-        Some(id)
+        let res = TypeAlias { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TypeAlias(res));
+        Some(ast_id)
     }
 
-    fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> {
+    fn lower_static(&mut self, static_: &ast::Static) -> Option<ItemTreeAstId<Static>> {
         let name = static_.name()?.as_name();
         let visibility = self.lower_visibility(static_);
         let ast_id = self.source_ast_id_map.ast_id(static_);
-        let res = Static { name, visibility, ast_id };
-        Some(id(self.data().statics.alloc(res)))
+        let res = Static { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Static(res));
+        Some(ast_id)
     }
 
-    fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
+    fn lower_const(&mut self, konst: &ast::Const) -> ItemTreeAstId<Const> {
         let name = konst.name().map(|it| it.as_name());
         let visibility = self.lower_visibility(konst);
         let ast_id = self.source_ast_id_map.ast_id(konst);
-        let res = Const { name, visibility, ast_id };
-        id(self.data().consts.alloc(res))
+        let res = Const { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Const(res));
+        ast_id
     }
 
-    fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
+    fn lower_module(&mut self, module: &ast::Module) -> Option<ItemTreeAstId<Mod>> {
         let name = module.name()?.as_name();
         let visibility = self.lower_visibility(module);
         let kind = if module.semicolon_token().is_some() {
@@ -381,70 +251,59 @@
             }
         };
         let ast_id = self.source_ast_id_map.ast_id(module);
-        let res = Mod { name, visibility, kind, ast_id };
-        Some(id(self.data().mods.alloc(res)))
+        let res = Mod { name, visibility, kind };
+        self.tree.big_data.insert(ast_id.upcast(), BigModItem::Mod(res));
+        Some(ast_id)
     }
 
-    fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<FileItemTreeId<Trait>> {
+    fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<ItemTreeAstId<Trait>> {
         let name = trait_def.name()?.as_name();
         let visibility = self.lower_visibility(trait_def);
         let ast_id = self.source_ast_id_map.ast_id(trait_def);
 
-        let items = trait_def
-            .assoc_item_list()
-            .into_iter()
-            .flat_map(|list| list.assoc_items())
-            .filter_map(|item_node| self.lower_assoc_item(&item_node))
-            .collect();
-
-        let def = Trait { name, visibility, items, ast_id };
-        let id = id(self.data().traits.alloc(def));
-        Some(id)
+        let def = Trait { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Trait(def));
+        Some(ast_id)
     }
 
     fn lower_trait_alias(
         &mut self,
         trait_alias_def: &ast::TraitAlias,
-    ) -> Option<FileItemTreeId<TraitAlias>> {
+    ) -> Option<ItemTreeAstId<TraitAlias>> {
         let name = trait_alias_def.name()?.as_name();
         let visibility = self.lower_visibility(trait_alias_def);
         let ast_id = self.source_ast_id_map.ast_id(trait_alias_def);
 
-        let alias = TraitAlias { name, visibility, ast_id };
-        let id = id(self.data().trait_aliases.alloc(alias));
-        Some(id)
+        let alias = TraitAlias { name, visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TraitAlias(alias));
+        Some(ast_id)
     }
 
-    fn lower_impl(&mut self, impl_def: &ast::Impl) -> FileItemTreeId<Impl> {
+    fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId<Impl> {
         let ast_id = self.source_ast_id_map.ast_id(impl_def);
-        // We cannot use `assoc_items()` here as that does not include macro calls.
-        let items = impl_def
-            .assoc_item_list()
-            .into_iter()
-            .flat_map(|it| it.assoc_items())
-            .filter_map(|item| self.lower_assoc_item(&item))
-            .collect();
         // Note that trait impls don't get implicit `Self` unlike traits, because here they are a
         // type alias rather than a type parameter, so this is handled by the resolver.
-        let res = Impl { items, ast_id };
-        id(self.data().impls.alloc(res))
+        let res = Impl {};
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Impl(res));
+        ast_id
     }
 
-    fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Use>> {
+    fn lower_use(&mut self, use_item: &ast::Use) -> Option<ItemTreeAstId<Use>> {
         let visibility = self.lower_visibility(use_item);
         let ast_id = self.source_ast_id_map.ast_id(use_item);
         let (use_tree, _) = lower_use_tree(self.db, use_item.use_tree()?, &mut |range| {
             self.span_map().span_for_range(range).ctx
         })?;
 
-        let res = Use { visibility, ast_id, use_tree };
-        Some(id(self.data().uses.alloc(res)))
+        let res = Use { visibility, use_tree };
+        self.tree.big_data.insert(ast_id.upcast(), BigModItem::Use(res));
+        Some(ast_id)
     }
 
     fn lower_extern_crate(
         &mut self,
         extern_crate: &ast::ExternCrate,
-    ) -> Option<FileItemTreeId<ExternCrate>> {
+    ) -> Option<ItemTreeAstId<ExternCrate>> {
         let name = extern_crate.name_ref()?.as_name();
         let alias = extern_crate.rename().map(|a| {
             a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
@@ -452,11 +311,12 @@
         let visibility = self.lower_visibility(extern_crate);
         let ast_id = self.source_ast_id_map.ast_id(extern_crate);
 
-        let res = ExternCrate { name, alias, visibility, ast_id };
-        Some(id(self.data().extern_crates.alloc(res)))
+        let res = ExternCrate { name, alias, visibility };
+        self.tree.big_data.insert(ast_id.upcast(), BigModItem::ExternCrate(res));
+        Some(ast_id)
     }
 
-    fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
+    fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<ItemTreeAstId<MacroCall>> {
         let span_map = self.span_map();
         let path = m.path()?;
         let range = path.syntax().text_range();
@@ -465,31 +325,33 @@
         })?);
         let ast_id = self.source_ast_id_map.ast_id(m);
         let expand_to = hir_expand::ExpandTo::from_call_site(m);
-        let res = MacroCall { path, ast_id, expand_to, ctxt: span_map.span_for_range(range).ctx };
-        Some(id(self.data().macro_calls.alloc(res)))
+        let res = MacroCall { path, expand_to, ctxt: span_map.span_for_range(range).ctx };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroCall(res));
+        Some(ast_id)
     }
 
-    fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<FileItemTreeId<MacroRules>> {
+    fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<ItemTreeAstId<MacroRules>> {
         let name = m.name()?;
         let ast_id = self.source_ast_id_map.ast_id(m);
 
-        let res = MacroRules { name: name.as_name(), ast_id };
-        Some(id(self.data().macro_rules.alloc(res)))
+        let res = MacroRules { name: name.as_name() };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroRules(res));
+        Some(ast_id)
     }
 
-    fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<FileItemTreeId<Macro2>> {
+    fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<ItemTreeAstId<Macro2>> {
         let name = m.name()?;
 
         let ast_id = self.source_ast_id_map.ast_id(m);
         let visibility = self.lower_visibility(m);
 
-        let res = Macro2 { name: name.as_name(), ast_id, visibility };
-        Some(id(self.data().macro_defs.alloc(res)))
+        let res = Macro2 { name: name.as_name(), visibility };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Macro2(res));
+        Some(ast_id)
     }
 
-    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> FileItemTreeId<ExternBlock> {
+    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> ItemTreeAstId<ExternBlock> {
         let ast_id = self.source_ast_id_map.ast_id(block);
-        let abi = block.abi().map(lower_abi);
         let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| {
             list.extern_items()
                 .filter_map(|item| {
@@ -497,42 +359,44 @@
                     // (in other words, the knowledge that they're in an extern block must not be used).
                     // This is because an extern block can contain macros whose ItemTree's top-level items
                     // should be considered to be in an extern block too.
-                    let mod_item: ModItem = match &item {
+                    let mod_item: ModItemId = match &item {
                         ast::ExternItem::Fn(ast) => self.lower_function(ast)?.into(),
                         ast::ExternItem::Static(ast) => self.lower_static(ast)?.into(),
                         ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
                         ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
                     };
                     let attrs = RawAttrs::new(self.db, &item, self.span_map());
-                    self.add_attrs(mod_item.into(), attrs);
+                    self.add_attrs(mod_item.ast_id(), attrs);
                     Some(mod_item)
                 })
                 .collect()
         });
 
-        let res = ExternBlock { abi, ast_id, children };
-        id(self.data().extern_blocks.alloc(res))
+        let res = ExternBlock { children };
+        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::ExternBlock(res));
+        ast_id
     }
 
     fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
         let vis = visibility_from_ast(self.db, item.visibility(), &mut |range| {
             self.span_map().span_for_range(range).ctx
         });
-        self.data().vis.alloc(vis)
-    }
-
-    fn next_variant_idx(&self) -> Idx<Variant> {
-        Idx::from_raw(RawIdx::from(
-            self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
-        ))
-    }
-}
-
-fn lower_abi(abi: ast::Abi) -> Symbol {
-    match abi.abi_string() {
-        Some(tok) => Symbol::intern(tok.text_without_quotes()),
-        // `extern` default to be `extern "C"`.
-        _ => sym::C,
+        match &vis {
+            RawVisibility::Public => RawVisibilityId::PUB,
+            RawVisibility::Module(path, explicitness) if path.segments().is_empty() => {
+                match (path.kind, explicitness) {
+                    (PathKind::SELF, VisibilityExplicitness::Explicit) => {
+                        RawVisibilityId::PRIV_EXPLICIT
+                    }
+                    (PathKind::SELF, VisibilityExplicitness::Implicit) => {
+                        RawVisibilityId::PRIV_IMPLICIT
+                    }
+                    (PathKind::Crate, _) => RawVisibilityId::PUB_CRATE,
+                    _ => RawVisibilityId(self.visibilities.insert_full(vis).0 as u32),
+                }
+            }
+            _ => RawVisibilityId(self.visibilities.insert_full(vis).0 as u32),
+        }
     }
 }
 
@@ -561,17 +425,15 @@
                 }
             };
 
+            self.mapping.alloc(tree.clone());
             let list = use_tree_list
                 .use_trees()
                 .filter_map(|tree| self.lower_use_tree(tree, span_for_range))
                 .collect();
 
-            Some(
-                self.use_tree(
-                    UseTreeKind::Prefixed { prefix: prefix.map(Interned::new), list },
-                    tree,
-                ),
-            )
+            Some(UseTree {
+                kind: UseTreeKind::Prefixed { prefix: prefix.map(Interned::new), list },
+            })
         } else {
             let is_glob = tree.star_token().is_some();
             let path = match tree.path() {
@@ -590,23 +452,20 @@
                     if path.is_none() {
                         cov_mark::hit!(glob_enum_group);
                     }
-                    Some(self.use_tree(UseTreeKind::Glob { path: path.map(Interned::new) }, tree))
+                    self.mapping.alloc(tree.clone());
+                    Some(UseTree { kind: UseTreeKind::Glob { path: path.map(Interned::new) } })
                 }
                 // Globs can't be renamed
                 (_, Some(_), true) | (None, None, false) => None,
                 // `bla::{ as Name}` is invalid
                 (None, Some(_), false) => None,
-                (Some(path), alias, false) => Some(
-                    self.use_tree(UseTreeKind::Single { path: Interned::new(path), alias }, tree),
-                ),
+                (Some(path), alias, false) => {
+                    self.mapping.alloc(tree.clone());
+                    Some(UseTree { kind: UseTreeKind::Single { path: Interned::new(path), alias } })
+                }
             }
         }
     }
-
-    fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree {
-        let index = self.mapping.alloc(ast);
-        UseTree { index, kind }
-    }
 }
 
 pub(crate) fn lower_use_tree(
@@ -626,7 +485,7 @@
     )
 }
 
-fn visibility_from_ast(
+pub(crate) fn visibility_from_ast(
     db: &dyn DefDatabase,
     node: Option<ast::Visibility>,
     span_for_range: &mut dyn FnMut(::tt::TextRange) -> SyntaxContext,
@@ -647,3 +506,11 @@
     };
     RawVisibility::Module(Interned::new(path), VisibilityExplicitness::Explicit)
 }
+
+fn adt_shape(kind: StructKind) -> FieldsShape {
+    match kind {
+        StructKind::Record(_) => FieldsShape::Record,
+        StructKind::Tuple(_) => FieldsShape::Tuple,
+        StructKind::Unit => FieldsShape::Unit,
+    }
+}
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 47c6eb1..696174c 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -2,15 +2,13 @@
 
 use std::fmt::{self, Write};
 
-use la_arena::{Idx, RawIdx};
 use span::{Edition, ErasedFileAstId};
 
 use crate::{
     item_tree::{
-        AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldParent,
-        FieldsShape, FileItemTreeId, Function, Impl, ItemTree, Macro2, MacroCall, MacroRules, Mod,
-        ModItem, ModKind, RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias,
-        Union, Use, UseTree, UseTreeKind, Variant,
+        Const, DefDatabase, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree,
+        Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawAttrs, RawVisibilityId, Static,
+        Struct, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind,
     },
     visibility::RawVisibility,
 };
@@ -19,9 +17,7 @@
     let mut p =
         Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true, edition };
 
-    if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
-        p.print_attrs(attrs, true, "\n");
-    }
+    p.print_attrs(&tree.top_attrs, true, "\n");
     p.blank();
 
     for item in tree.top_level_items() {
@@ -103,8 +99,8 @@
         }
     }
 
-    fn print_attrs_of(&mut self, of: impl Into<AttrOwner>, separated_by: &str) {
-        if let Some(attrs) = self.tree.attrs.get(&of.into()) {
+    fn print_attrs_of(&mut self, of: ModItemId, separated_by: &str) {
+        if let Some(attrs) = self.tree.attrs.get(&of.ast_id()) {
             self.print_attrs(attrs, false, separated_by);
         }
     }
@@ -112,50 +108,22 @@
     fn print_visibility(&mut self, vis: RawVisibilityId) {
         match &self.tree[vis] {
             RawVisibility::Module(path, _expl) => {
-                w!(self, "pub({}) ", path.display(self.db, self.edition))
+                w!(self, "pub(in {}) ", path.display(self.db, self.edition))
             }
             RawVisibility::Public => w!(self, "pub "),
+            RawVisibility::PubCrate => w!(self, "pub(crate) "),
+            RawVisibility::PubSelf(_) => w!(self, "pub(self) "),
         };
     }
 
-    fn print_fields(&mut self, parent: FieldParent, kind: FieldsShape, fields: &[Field]) {
-        let edition = self.edition;
+    fn print_fields(&mut self, kind: FieldsShape) {
         match kind {
             FieldsShape::Record => {
                 self.whitespace();
-                w!(self, "{{");
-                self.indented(|this| {
-                    for (idx, Field { name, visibility, is_unsafe }) in fields.iter().enumerate() {
-                        this.print_attrs_of(
-                            AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
-                            "\n",
-                        );
-                        this.print_visibility(*visibility);
-                        if *is_unsafe {
-                            w!(this, "unsafe ");
-                        }
-
-                        wln!(this, "{},", name.display(self.db, edition));
-                    }
-                });
-                w!(self, "}}");
+                w!(self, "{{ ... }}");
             }
             FieldsShape::Tuple => {
-                w!(self, "(");
-                self.indented(|this| {
-                    for (idx, Field { name, visibility, is_unsafe }) in fields.iter().enumerate() {
-                        this.print_attrs_of(
-                            AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
-                            "\n",
-                        );
-                        this.print_visibility(*visibility);
-                        if *is_unsafe {
-                            w!(this, "unsafe ");
-                        }
-                        wln!(this, "{},", name.display(self.db, edition));
-                    }
-                });
-                w!(self, ")");
+                w!(self, "(...)");
             }
             FieldsShape::Unit => {}
         }
@@ -191,20 +159,20 @@
         }
     }
 
-    fn print_mod_item(&mut self, item: ModItem) {
+    fn print_mod_item(&mut self, item: ModItemId) {
         self.print_attrs_of(item, "\n");
 
         match item {
-            ModItem::Use(it) => {
-                let Use { visibility, use_tree, ast_id } = &self.tree[it];
+            ModItemId::Use(ast_id) => {
+                let Use { visibility, use_tree } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "use ");
                 self.print_use_tree(use_tree);
                 wln!(self, ";");
             }
-            ModItem::ExternCrate(it) => {
-                let ExternCrate { name, alias, visibility, ast_id } = &self.tree[it];
+            ModItemId::ExternCrate(ast_id) => {
+                let ExternCrate { name, alias, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "extern crate {}", name.display(self.db, self.edition));
@@ -213,14 +181,10 @@
                 }
                 wln!(self, ";");
             }
-            ModItem::ExternBlock(it) => {
-                let ExternBlock { abi, ast_id, children } = &self.tree[it];
+            ModItemId::ExternBlock(ast_id) => {
+                let ExternBlock { children } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
-                w!(self, "extern ");
-                if let Some(abi) = abi {
-                    w!(self, "\"{}\" ", abi);
-                }
-                w!(self, "{{");
+                w!(self, "extern {{");
                 self.indented(|this| {
                     for child in &**children {
                         this.print_mod_item(*child);
@@ -228,52 +192,40 @@
                 });
                 wln!(self, "}}");
             }
-            ModItem::Function(it) => {
-                let Function { name, visibility, ast_id } = &self.tree[it];
+            ModItemId::Function(ast_id) => {
+                let Function { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 wln!(self, "fn {};", name.display(self.db, self.edition));
             }
-            ModItem::Struct(it) => {
-                let Struct { visibility, name, fields, shape: kind, ast_id } = &self.tree[it];
+            ModItemId::Struct(ast_id) => {
+                let Struct { visibility, name, shape: kind } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "struct {}", name.display(self.db, self.edition));
-                self.print_fields(FieldParent::Struct(it), *kind, fields);
+                self.print_fields(*kind);
                 if matches!(kind, FieldsShape::Record) {
                     wln!(self);
                 } else {
                     wln!(self, ";");
                 }
             }
-            ModItem::Union(it) => {
-                let Union { name, visibility, fields, ast_id } = &self.tree[it];
+            ModItemId::Union(ast_id) => {
+                let Union { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "union {}", name.display(self.db, self.edition));
-                self.print_fields(FieldParent::Union(it), FieldsShape::Record, fields);
+                self.print_fields(FieldsShape::Record);
                 wln!(self);
             }
-            ModItem::Enum(it) => {
-                let Enum { name, visibility, variants, ast_id } = &self.tree[it];
+            ModItemId::Enum(ast_id) => {
+                let Enum { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "enum {}", name.display(self.db, self.edition));
-                let edition = self.edition;
-                self.indented(|this| {
-                    for variant in FileItemTreeId::range_iter(variants.clone()) {
-                        let Variant { name, fields, shape: kind, ast_id } = &this.tree[variant];
-                        this.print_ast_id(ast_id.erase());
-                        this.print_attrs_of(variant, "\n");
-                        w!(this, "{}", name.display(self.db, edition));
-                        this.print_fields(FieldParent::EnumVariant(variant), *kind, fields);
-                        wln!(this, ",");
-                    }
-                });
-                wln!(self, "}}");
+                w!(self, "enum {} {{ ... }}", name.display(self.db, self.edition));
             }
-            ModItem::Const(it) => {
-                let Const { name, visibility, ast_id } = &self.tree[it];
+            ModItemId::Const(ast_id) => {
+                let Const { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "const ");
@@ -283,8 +235,8 @@
                 }
                 wln!(self, " = _;");
             }
-            ModItem::Static(it) => {
-                let Static { name, visibility, ast_id } = &self.tree[it];
+            ModItemId::Static(ast_id) => {
+                let Static { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "static ");
@@ -292,45 +244,33 @@
                 w!(self, " = _;");
                 wln!(self);
             }
-            ModItem::Trait(it) => {
-                let Trait { name, visibility, items, ast_id } = &self.tree[it];
+            ModItemId::Trait(ast_id) => {
+                let Trait { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "trait {} {{", name.display(self.db, self.edition));
-                self.indented(|this| {
-                    for item in &**items {
-                        this.print_mod_item((*item).into());
-                    }
-                });
-                wln!(self, "}}");
+                w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition));
             }
-            ModItem::TraitAlias(it) => {
-                let TraitAlias { name, visibility, ast_id } = &self.tree[it];
+            ModItemId::TraitAlias(ast_id) => {
+                let TraitAlias { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 wln!(self, "trait {} = ..;", name.display(self.db, self.edition));
             }
-            ModItem::Impl(it) => {
-                let Impl { items, ast_id } = &self.tree[it];
+            ModItemId::Impl(ast_id) => {
+                let Impl {} = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
-                w!(self, "impl {{");
-                self.indented(|this| {
-                    for item in &**items {
-                        this.print_mod_item((*item).into());
-                    }
-                });
-                wln!(self, "}}");
+                w!(self, "impl {{ ... }}");
             }
-            ModItem::TypeAlias(it) => {
-                let TypeAlias { name, visibility, ast_id } = &self.tree[it];
+            ModItemId::TypeAlias(ast_id) => {
+                let TypeAlias { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "type {}", name.display(self.db, self.edition));
                 w!(self, ";");
                 wln!(self);
             }
-            ModItem::Mod(it) => {
-                let Mod { name, visibility, kind, ast_id } = &self.tree[it];
+            ModItemId::Mod(ast_id) => {
+                let Mod { name, visibility, kind } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "mod {}", name.display(self.db, self.edition));
@@ -349,24 +289,24 @@
                     }
                 }
             }
-            ModItem::MacroCall(it) => {
-                let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it];
+            ModItemId::MacroCall(ast_id) => {
+                let MacroCall { path, expand_to, ctxt } = &self.tree[ast_id];
                 let _ = writeln!(
                     self,
-                    "// AstId: {:?}, SyntaxContextId: {}, ExpandTo: {:?}",
-                    ast_id.erase().into_raw(),
+                    "// AstId: {:#?}, SyntaxContextId: {}, ExpandTo: {:?}",
+                    ast_id.erase(),
                     ctxt,
                     expand_to
                 );
                 wln!(self, "{}!(...);", path.display(self.db, self.edition));
             }
-            ModItem::MacroRules(it) => {
-                let MacroRules { name, ast_id } = &self.tree[it];
+            ModItemId::MacroRules(ast_id) => {
+                let MacroRules { name } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db, self.edition));
             }
-            ModItem::Macro2(it) => {
-                let Macro2 { name, visibility, ast_id } = &self.tree[it];
+            ModItemId::Macro2(ast_id) => {
+                let Macro2 { name, visibility } = &self.tree[ast_id];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 wln!(self, "macro {} {{ ... }}", name.display(self.db, self.edition));
@@ -377,7 +317,7 @@
     }
 
     fn print_ast_id(&mut self, ast_id: ErasedFileAstId) {
-        wln!(self, "// AstId: {:?}", ast_id.into_raw());
+        wln!(self, "// AstId: {ast_id:#?}");
     }
 }
 
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 824fbfa..5923b3e 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -35,23 +35,23 @@
             #![no_std]
             #![doc = " another file comment"]
 
-            // AstId: 1
+            // AstId: ExternCrate[5A82, 0]
             pub(self) extern crate self as renamed;
 
-            // AstId: 2
-            pub(super) extern crate bli;
+            // AstId: ExternCrate[7E1C, 0]
+            pub(in super) extern crate bli;
 
-            // AstId: 3
+            // AstId: Use[0000, 0]
             pub use crate::path::{nested, items as renamed, Trait as _};
 
-            // AstId: 4
+            // AstId: Use[0000, 1]
             pub(self) use globs::*;
 
             #[doc = " docs on import"]
-            // AstId: 5
+            // AstId: Use[0000, 2]
             pub(self) use crate::{A, B};
 
-            // AstId: 6
+            // AstId: Use[0000, 3]
             pub(self) use a::{c, d::{e}};
         "##]],
     );
@@ -73,23 +73,23 @@
     fn ex_fn();
 }
         "#,
-        expect![[r##"
+        expect![[r#"
             #[on_extern_block]
-            // AstId: 1
-            extern "C" {
+            // AstId: ExternBlock[0000, 0]
+            extern {
                 #[on_extern_type]
-                // AstId: 2
+                // AstId: TypeAlias[9FDF, 0]
                 pub(self) type ExType;
 
                 #[on_extern_static]
-                // AstId: 3
+                // AstId: Static[43C1, 0]
                 pub(self) static EX_STATIC = _;
 
                 #[on_extern_fn]
-                // AstId: 4
+                // AstId: Fn[452D, 0]
                 pub(self) fn ex_fn;
             }
-        "##]],
+        "#]],
     );
 }
 
@@ -124,44 +124,21 @@
 }
         "#,
         expect![[r#"
-            // AstId: 1
+            // AstId: Struct[DFF3, 0]
             pub(self) struct Unit;
 
             #[derive(Debug)]
-            // AstId: 2
-            pub(self) struct Struct {
-                #[doc = " fld docs"]
-                pub(self) fld,
-            }
+            // AstId: Struct[C7A1, 0]
+            pub(self) struct Struct { ... }
 
-            // AstId: 3
-            pub(self) struct Tuple(
-                #[attr]
-                pub(self) 0,
-            );
+            // AstId: Struct[DAC2, 0]
+            pub(self) struct Tuple(...);
 
-            // AstId: 4
-            pub(self) union Ize {
-                pub(self) a,
-                pub(self) b,
-            }
+            // AstId: Union[2DBB, 0]
+            pub(self) union Ize { ... }
 
-            // AstId: 5
-            pub(self) enum E
-                // AstId: 6
-                #[doc = " comment on Unit"]
-                Unit,
-                // AstId: 7
-                #[doc = " comment on Tuple"]
-                Tuple(
-                    pub(self) 0,
-                ),
-                // AstId: 8
-                Struct {
-                    #[doc = " comment on a: u8"]
-                    pub(self) a,
-                },
-            }
+            // AstId: Enum[7FF8, 0]
+            pub(self) enum E { ... }
         "#]],
     );
 }
@@ -185,25 +162,19 @@
 }
         "#,
         expect![[r#"
-            // AstId: 1
+            // AstId: Static[B393, 0]
             pub static ST = _;
 
-            // AstId: 2
+            // AstId: Const[B309, 0]
             pub(self) const _ = _;
 
             #[attr]
             #[inner_attr_in_fn]
-            // AstId: 3
+            // AstId: Fn[75E3, 0]
             pub(self) fn f;
 
-            // AstId: 4
-            pub(self) trait Tr {
-                // AstId: 6
-                pub(self) type Assoc;
-
-                // AstId: 7
-                pub(self) fn method;
-            }
+            // AstId: Trait[2998, 0]
+            pub(self) trait Tr { ... }
         "#]],
     );
 }
@@ -226,16 +197,16 @@
         expect![[r##"
             #[doc = " outer"]
             #[doc = " inner"]
-            // AstId: 1
+            // AstId: Module[CF93, 0]
             pub(self) mod inline {
-                // AstId: 3
+                // AstId: Use[0000, 0]
                 pub(self) use super::*;
 
-                // AstId: 4
+                // AstId: Fn[1B26, 0]
                 pub(self) fn fn_in_module;
             }
 
-            // AstId: 2
+            // AstId: Module[8994, 0]
             pub(self) mod outline;
         "##]],
     );
@@ -254,13 +225,13 @@
 m!();
         "#,
         expect![[r#"
-            // AstId: 1
+            // AstId: MacroRules[88CE, 0]
             macro_rules! m { ... }
 
-            // AstId: 2
+            // AstId: MacroDef[DC34, 0]
             pub macro m2 { ... }
 
-            // AstId: 3, SyntaxContextId: ROOT2024, ExpandTo: Items
+            // AstId: MacroCall[612F, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
             m!(...);
         "#]],
     );
@@ -273,7 +244,7 @@
 pub(self) struct S;
         "#,
         expect![[r#"
-            // AstId: 1
+            // AstId: Struct[42E2, 0]
             pub(self) struct S;
         "#]],
     )
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 4ad4477..faff7d0 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -96,7 +96,7 @@
     for (_, module_data) in crate_def_map.modules() {
         for impl_def in module_data.scope.impls() {
             lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
-            for &(_, assoc) in db.impl_items(impl_def).items.iter() {
+            for &(_, assoc) in impl_def.impl_items(db).items.iter() {
                 match assoc {
                     AssocItemId::FunctionId(f) => {
                         lang_items.collect_lang_item(db, f, LangItemTarget::Function)
@@ -125,7 +125,7 @@
                 }
                 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                     lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
-                    db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
+                    e.enum_variants(db).variants.iter().for_each(|&(id, _, _)| {
                         lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
                     });
                 }
@@ -377,6 +377,7 @@
     AsyncFnMut,              sym::async_fn_mut,        async_fn_mut_trait,         Target::Trait,          GenericRequirement::Exact(1);
     AsyncFnOnce,             sym::async_fn_once,       async_fn_once_trait,        Target::Trait,          GenericRequirement::Exact(1);
 
+    AsyncFnOnceOutput,       sym::async_fn_once_output,async_fn_once_output,       Target::AssocTy,        GenericRequirement::None;
     FnOnceOutput,            sym::fn_once_output,      fn_once_output,             Target::AssocTy,        GenericRequirement::None;
 
     Future,                  sym::future_trait,        future_trait,               Target::Trait,          GenericRequirement::Exact(0);
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index b41ff02..a542214 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -49,8 +49,9 @@
 pub mod import_map;
 pub mod visibility;
 
-use intern::{Interned, sym};
+use intern::{Interned, Symbol, sym};
 pub use rustc_abi as layout;
+use thin_vec::ThinVec;
 use triomphe::Arc;
 
 pub use crate::signatures::LocalFieldId;
@@ -74,12 +75,11 @@
     name::Name,
     proc_macro::{CustomProcMacroExpander, ProcMacroKind},
 };
-use item_tree::ExternBlock;
 use la_arena::Idx;
 use nameres::DefMap;
 use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
 use stdx::impl_from;
-use syntax::ast;
+use syntax::{AstNode, ast};
 
 pub use hir_expand::{Intern, Lookup, tt};
 
@@ -88,12 +88,11 @@
     builtin_type::BuiltinType,
     db::DefDatabase,
     hir::generics::{LocalLifetimeParamId, LocalTypeOrConstParamId},
-    item_tree::{
-        Const, Enum, ExternCrate, Function, Impl, ItemTreeId, ItemTreeNode, Macro2, MacroRules,
-        Static, Struct, Trait, TraitAlias, TypeAlias, Union, Use, Variant,
+    nameres::{
+        LocalDefMap, assoc::ImplItems, block_def_map, crate_def_map, crate_local_def_map,
+        diagnostics::DefDiagnostics,
     },
-    nameres::{LocalDefMap, block_def_map, crate_def_map, crate_local_def_map},
-    signatures::VariantFields,
+    signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields},
 };
 
 type FxIndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>;
@@ -113,70 +112,111 @@
 }
 
 #[derive(Debug)]
-pub struct ItemLoc<N: ItemTreeNode> {
+pub struct ItemLoc<N: AstIdNode> {
     pub container: ModuleId,
-    pub id: ItemTreeId<N>,
+    pub id: AstId<N>,
 }
 
-impl<N: ItemTreeNode> Clone for ItemLoc<N> {
+impl<N: AstIdNode> Clone for ItemLoc<N> {
     fn clone(&self) -> Self {
         *self
     }
 }
 
-impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
+impl<N: AstIdNode> Copy for ItemLoc<N> {}
 
-impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
+impl<N: AstIdNode> PartialEq for ItemLoc<N> {
     fn eq(&self, other: &Self) -> bool {
         self.container == other.container && self.id == other.id
     }
 }
 
-impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
+impl<N: AstIdNode> Eq for ItemLoc<N> {}
 
-impl<N: ItemTreeNode> Hash for ItemLoc<N> {
+impl<N: AstIdNode> Hash for ItemLoc<N> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.container.hash(state);
         self.id.hash(state);
     }
 }
 
+impl<N: AstIdNode> HasModule for ItemLoc<N> {
+    #[inline]
+    fn module(&self, _db: &dyn DefDatabase) -> ModuleId {
+        self.container
+    }
+}
+
 #[derive(Debug)]
-pub struct AssocItemLoc<N: ItemTreeNode> {
+pub struct AssocItemLoc<N: AstIdNode> {
+    // FIXME: Store this as an erased `salsa::Id` to save space
     pub container: ItemContainerId,
-    pub id: ItemTreeId<N>,
+    pub id: AstId<N>,
 }
 
-impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
+impl<N: AstIdNode> Clone for AssocItemLoc<N> {
     fn clone(&self) -> Self {
         *self
     }
 }
 
-impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
+impl<N: AstIdNode> Copy for AssocItemLoc<N> {}
 
-impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
+impl<N: AstIdNode> PartialEq for AssocItemLoc<N> {
     fn eq(&self, other: &Self) -> bool {
         self.container == other.container && self.id == other.id
     }
 }
 
-impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
+impl<N: AstIdNode> Eq for AssocItemLoc<N> {}
 
-impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
+impl<N: AstIdNode> Hash for AssocItemLoc<N> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.container.hash(state);
         self.id.hash(state);
     }
 }
 
-pub trait ItemTreeLoc {
+impl<N: AstIdNode> HasModule for AssocItemLoc<N> {
+    #[inline]
+    fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+        self.container.module(db)
+    }
+}
+
+pub trait AstIdLoc {
     type Container;
-    type Id;
-    fn item_tree_id(&self) -> ItemTreeId<Self::Id>;
+    type Ast: AstNode;
+    fn ast_id(&self) -> AstId<Self::Ast>;
     fn container(&self) -> Self::Container;
 }
 
+impl<N: AstIdNode> AstIdLoc for ItemLoc<N> {
+    type Container = ModuleId;
+    type Ast = N;
+    #[inline]
+    fn ast_id(&self) -> AstId<Self::Ast> {
+        self.id
+    }
+    #[inline]
+    fn container(&self) -> Self::Container {
+        self.container
+    }
+}
+
+impl<N: AstIdNode> AstIdLoc for AssocItemLoc<N> {
+    type Container = ItemContainerId;
+    type Ast = N;
+    #[inline]
+    fn ast_id(&self) -> AstId<Self::Ast> {
+        self.id
+    }
+    #[inline]
+    fn container(&self) -> Self::Container {
+        self.container
+    }
+}
+
 macro_rules! impl_intern {
     ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
         impl_intern_key!($id, $loc);
@@ -186,74 +226,103 @@
 
 macro_rules! impl_loc {
     ($loc:ident, $id:ident: $id_ty:ident, $container:ident: $container_type:ident) => {
-        impl ItemTreeLoc for $loc {
+        impl AstIdLoc for $loc {
             type Container = $container_type;
-            type Id = $id_ty;
-            fn item_tree_id(&self) -> ItemTreeId<Self::Id> {
+            type Ast = ast::$id_ty;
+            fn ast_id(&self) -> AstId<Self::Ast> {
                 self.$id
             }
             fn container(&self) -> Self::Container {
                 self.$container
             }
         }
+
+        impl HasModule for $loc {
+            #[inline]
+            fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+                self.$container.module(db)
+            }
+        }
     };
 }
 
-type FunctionLoc = AssocItemLoc<Function>;
+type FunctionLoc = AssocItemLoc<ast::Fn>;
 impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
-impl_loc!(FunctionLoc, id: Function, container: ItemContainerId);
 
-type StructLoc = ItemLoc<Struct>;
+type StructLoc = ItemLoc<ast::Struct>;
 impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
-impl_loc!(StructLoc, id: Struct, container: ModuleId);
 
-pub type UnionLoc = ItemLoc<Union>;
+pub type UnionLoc = ItemLoc<ast::Union>;
 impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
-impl_loc!(UnionLoc, id: Union, container: ModuleId);
 
-pub type EnumLoc = ItemLoc<Enum>;
+pub type EnumLoc = ItemLoc<ast::Enum>;
 impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
-impl_loc!(EnumLoc, id: Enum, container: ModuleId);
 
-type ConstLoc = AssocItemLoc<Const>;
+impl EnumId {
+    #[inline]
+    pub fn enum_variants(self, db: &dyn DefDatabase) -> &EnumVariants {
+        &self.enum_variants_with_diagnostics(db).0
+    }
+
+    #[inline]
+    pub fn enum_variants_with_diagnostics(
+        self,
+        db: &dyn DefDatabase,
+    ) -> &(EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
+        EnumVariants::of(db, self)
+    }
+}
+
+type ConstLoc = AssocItemLoc<ast::Const>;
 impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
-impl_loc!(ConstLoc, id: Const, container: ItemContainerId);
 
-pub type StaticLoc = AssocItemLoc<Static>;
+pub type StaticLoc = AssocItemLoc<ast::Static>;
 impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
-impl_loc!(StaticLoc, id: Static, container: ItemContainerId);
 
-pub type TraitLoc = ItemLoc<Trait>;
+pub type TraitLoc = ItemLoc<ast::Trait>;
 impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
-impl_loc!(TraitLoc, id: Trait, container: ModuleId);
 
-pub type TraitAliasLoc = ItemLoc<TraitAlias>;
+pub type TraitAliasLoc = ItemLoc<ast::TraitAlias>;
 impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
-impl_loc!(TraitAliasLoc, id: TraitAlias, container: ModuleId);
 
-type TypeAliasLoc = AssocItemLoc<TypeAlias>;
+type TypeAliasLoc = AssocItemLoc<ast::TypeAlias>;
 impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
-impl_loc!(TypeAliasLoc, id: TypeAlias, container: ItemContainerId);
 
-type ImplLoc = ItemLoc<Impl>;
+type ImplLoc = ItemLoc<ast::Impl>;
 impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
-impl_loc!(ImplLoc, id: Impl, container: ModuleId);
 
-type UseLoc = ItemLoc<Use>;
+impl ImplId {
+    #[inline]
+    pub fn impl_items(self, db: &dyn DefDatabase) -> &ImplItems {
+        &self.impl_items_with_diagnostics(db).0
+    }
+
+    #[inline]
+    pub fn impl_items_with_diagnostics(self, db: &dyn DefDatabase) -> &(ImplItems, DefDiagnostics) {
+        ImplItems::of(db, self)
+    }
+}
+
+type UseLoc = ItemLoc<ast::Use>;
 impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
-impl_loc!(UseLoc, id: Use, container: ModuleId);
 
-type ExternCrateLoc = ItemLoc<ExternCrate>;
+type ExternCrateLoc = ItemLoc<ast::ExternCrate>;
 impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
-impl_loc!(ExternCrateLoc, id: ExternCrate, container: ModuleId);
 
-type ExternBlockLoc = ItemLoc<ExternBlock>;
+type ExternBlockLoc = ItemLoc<ast::ExternBlock>;
 impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block);
-impl_loc!(ExternBlockLoc, id: ExternBlock, container: ModuleId);
+
+#[salsa::tracked]
+impl ExternBlockId {
+    #[salsa::tracked]
+    pub fn abi(self, db: &dyn DefDatabase) -> Option<Symbol> {
+        signatures::extern_block_abi(db, self)
+    }
+}
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct EnumVariantLoc {
-    pub id: ItemTreeId<Variant>,
+    pub id: AstId<ast::Variant>,
     pub parent: EnumId,
     pub index: u32,
 }
@@ -262,18 +331,18 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Macro2Loc {
     pub container: ModuleId,
-    pub id: ItemTreeId<Macro2>,
+    pub id: AstId<ast::MacroDef>,
     pub expander: MacroExpander,
     pub allow_internal_unsafe: bool,
     pub edition: Edition,
 }
 impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
-impl_loc!(Macro2Loc, id: Macro2, container: ModuleId);
+impl_loc!(Macro2Loc, id: MacroDef, container: ModuleId);
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct MacroRulesLoc {
     pub container: ModuleId,
-    pub id: ItemTreeId<MacroRules>,
+    pub id: AstId<ast::MacroRules>,
     pub expander: MacroExpander,
     pub flags: MacroRulesLocFlags,
     pub edition: Edition,
@@ -301,13 +370,13 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ProcMacroLoc {
     pub container: CrateRootModuleId,
-    pub id: ItemTreeId<Function>,
+    pub id: AstId<ast::Fn>,
     pub expander: CustomProcMacroExpander,
     pub kind: ProcMacroKind,
     pub edition: Edition,
 }
 impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro);
-impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId);
+impl_loc!(ProcMacroLoc, id: Fn, container: CrateRootModuleId);
 
 #[derive(Debug, Hash, PartialEq, Eq, Clone)]
 pub struct BlockLoc {
@@ -315,7 +384,26 @@
     /// The containing module.
     pub module: ModuleId,
 }
-impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
+#[salsa_macros::tracked(debug)]
+#[derive(PartialOrd, Ord)]
+pub struct BlockIdLt<'db> {
+    pub loc: BlockLoc,
+}
+pub type BlockId = BlockIdLt<'static>;
+impl hir_expand::Intern for BlockLoc {
+    type Database = dyn DefDatabase;
+    type ID = BlockId;
+    fn intern(self, db: &Self::Database) -> Self::ID {
+        unsafe { std::mem::transmute::<BlockIdLt<'_>, BlockId>(BlockIdLt::new(db, self)) }
+    }
+}
+impl hir_expand::Lookup for BlockId {
+    type Database = dyn DefDatabase;
+    type Data = BlockLoc;
+    fn lookup(&self, db: &Self::Database) -> Self::Data {
+        self.loc(db)
+    }
+}
 
 /// A `ModuleId` that is always a crate's root module.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -338,6 +426,18 @@
     }
 }
 
+impl HasModule for CrateRootModuleId {
+    #[inline]
+    fn module(&self, _db: &dyn DefDatabase) -> ModuleId {
+        ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT }
+    }
+
+    #[inline]
+    fn krate(&self, _db: &dyn DefDatabase) -> Crate {
+        self.krate
+    }
+}
+
 impl PartialEq<ModuleId> for CrateRootModuleId {
     fn eq(&self, other: &ModuleId) -> bool {
         other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
@@ -466,11 +566,19 @@
     }
 }
 
+impl HasModule for ModuleId {
+    #[inline]
+    fn module(&self, _db: &dyn DefDatabase) -> ModuleId {
+        *self
+    }
+}
+
 /// An ID of a module, **local** to a `DefMap`.
 pub type LocalModuleId = Idx<nameres::ModuleData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct FieldId {
+    // FIXME: Store this as an erased `salsa::Id` to save space
     pub parent: VariantId,
     pub local_id: LocalFieldId,
 }
@@ -486,6 +594,7 @@
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct TypeOrConstParamId {
+    // FIXME: Store this as an erased `salsa::Id` to save space
     pub parent: GenericDefId,
     pub local_id: LocalTypeOrConstParamId,
 }
@@ -544,6 +653,7 @@
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct LifetimeParamId {
+    // FIXME: Store this as an erased `salsa::Id` to save space
     pub parent: GenericDefId,
     pub local_id: LocalLifetimeParamId,
 }
@@ -642,15 +752,10 @@
     pub fn name(self, db: &dyn DefDatabase) -> String {
         match self {
             GeneralConstId::StaticId(it) => {
-                let loc = it.lookup(db);
-                let tree = loc.item_tree_id().item_tree(db);
-                let name = tree[loc.id.value].name.display(db, Edition::CURRENT);
-                name.to_string()
+                db.static_signature(it).name.display(db, Edition::CURRENT).to_string()
             }
             GeneralConstId::ConstId(const_id) => {
-                let loc = const_id.lookup(db);
-                let tree = loc.item_tree_id().item_tree(db);
-                tree[loc.id.value].name.as_ref().map_or_else(
+                db.const_signature(const_id).name.as_ref().map_or_else(
                     || "_".to_owned(),
                     |name| name.display(db, Edition::CURRENT).to_string(),
                 )
@@ -692,7 +797,7 @@
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, salsa_macros::Supertype)]
 pub enum AssocItemId {
     FunctionId(FunctionId),
     ConstId(ConstId),
@@ -768,8 +873,8 @@
             GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it),
             GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
             GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
-            GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None),
-            GenericDefId::StaticId(it) => (it.lookup(db).id.file_id(), None),
+            GenericDefId::ConstId(it) => (it.lookup(db).id.file_id, None),
+            GenericDefId::StaticId(it) => (it.lookup(db).id.file_id, None),
         }
     }
 
@@ -935,9 +1040,9 @@
 
     pub fn file_id(self, db: &dyn DefDatabase) -> HirFileId {
         match self {
-            VariantId::EnumVariantId(it) => it.lookup(db).id.file_id(),
-            VariantId::StructId(it) => it.lookup(db).id.file_id(),
-            VariantId::UnionId(it) => it.lookup(db).id.file_id(),
+            VariantId::EnumVariantId(it) => it.lookup(db).id.file_id,
+            VariantId::StructId(it) => it.lookup(db).id.file_id,
+            VariantId::UnionId(it) => it.lookup(db).id.file_id,
         }
     }
 
@@ -977,7 +1082,7 @@
 
 impl<N, ItemId> HasModule for ItemId
 where
-    N: ItemTreeNode,
+    N: AstIdNode,
     ItemId: Lookup<Database = dyn DefDatabase, Data = ItemLoc<N>> + Copy,
 {
     #[inline]
@@ -1003,7 +1108,7 @@
 #[inline]
 fn module_for_assoc_item_loc<'db>(
     db: &(dyn 'db + DefDatabase),
-    id: impl Lookup<Database = dyn DefDatabase, Data = AssocItemLoc<impl ItemTreeNode>>,
+    id: impl Lookup<Database = dyn DefDatabase, Data = AssocItemLoc<impl AstIdNode>>,
 ) -> ModuleId {
     id.lookup(db).container.module(db)
 }
@@ -1245,7 +1350,7 @@
 // Crate authors can opt their type out of completions in some cases.
 // This is done with the `#[rust_analyzer::completions(...)]` attribute.
 //
-// All completeable things support `#[rust_analyzer::completions(ignore_flyimport)]`,
+// All completable things support `#[rust_analyzer::completions(ignore_flyimport)]`,
 // which causes the thing to get excluded from flyimport completion. It will still
 // be completed when in scope. This is analogous to the setting `rust-analyzer.completion.autoimport.exclude`
 // with `"type": "always"`.
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 38fc4b3..c6d901e 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@
     };
 }
 
-struct#0:1@58..64#14336# MyTraitMap2#0:2@31..42#ROOT2024# {#0:1@72..73#14336#
-    map#0:1@86..89#14336#:#0:1@89..90#14336# #0:1@89..90#14336#::#0:1@91..93#14336#std#0:1@93..96#14336#::#0:1@96..98#14336#collections#0:1@98..109#14336#::#0:1@109..111#14336#HashSet#0:1@111..118#14336#<#0:1@118..119#14336#(#0:1@119..120#14336#)#0:1@120..121#14336#>#0:1@121..122#14336#,#0:1@122..123#14336#
-}#0:1@132..133#14336#
+struct#0:MacroRules[8C8E, 0]@58..64#14336# MyTraitMap2#0:MacroCall[D499, 0]@31..42#ROOT2024# {#0:MacroRules[8C8E, 0]@72..73#14336#
+    map#0:MacroRules[8C8E, 0]@86..89#14336#:#0:MacroRules[8C8E, 0]@89..90#14336# #0:MacroRules[8C8E, 0]@89..90#14336#::#0:MacroRules[8C8E, 0]@91..93#14336#std#0:MacroRules[8C8E, 0]@93..96#14336#::#0:MacroRules[8C8E, 0]@96..98#14336#collections#0:MacroRules[8C8E, 0]@98..109#14336#::#0:MacroRules[8C8E, 0]@109..111#14336#HashSet#0:MacroRules[8C8E, 0]@111..118#14336#<#0:MacroRules[8C8E, 0]@118..119#14336#(#0:MacroRules[8C8E, 0]@119..120#14336#)#0:MacroRules[8C8E, 0]@120..121#14336#>#0:MacroRules[8C8E, 0]@121..122#14336#,#0:MacroRules[8C8E, 0]@122..123#14336#
+}#0:MacroRules[8C8E, 0]@132..133#14336#
 "#]],
     );
 }
@@ -75,12 +75,12 @@
     };
 }
 
-fn#0:2@30..32#ROOT2024# main#0:2@33..37#ROOT2024#(#0:2@37..38#ROOT2024#)#0:2@38..39#ROOT2024# {#0:2@40..41#ROOT2024#
-    1#0:2@50..51#ROOT2024#;#0:2@51..52#ROOT2024#
-    1.0#0:2@61..64#ROOT2024#;#0:2@64..65#ROOT2024#
-    (#0:2@74..75#ROOT2024#(#0:2@75..76#ROOT2024#1#0:2@76..77#ROOT2024#,#0:2@77..78#ROOT2024# )#0:2@78..79#ROOT2024#,#0:2@79..80#ROOT2024# )#0:2@80..81#ROOT2024#.#0:2@81..82#ROOT2024#0#0:2@82..85#ROOT2024#.#0:2@82..85#ROOT2024#0#0:2@82..85#ROOT2024#;#0:2@85..86#ROOT2024#
-    let#0:2@95..98#ROOT2024# x#0:2@99..100#ROOT2024# =#0:2@101..102#ROOT2024# 1#0:2@103..104#ROOT2024#;#0:2@104..105#ROOT2024#
-}#0:2@110..111#ROOT2024#
+fn#0:MacroCall[D499, 0]@30..32#ROOT2024# main#0:MacroCall[D499, 0]@33..37#ROOT2024#(#0:MacroCall[D499, 0]@37..38#ROOT2024#)#0:MacroCall[D499, 0]@38..39#ROOT2024# {#0:MacroCall[D499, 0]@40..41#ROOT2024#
+    1#0:MacroCall[D499, 0]@50..51#ROOT2024#;#0:MacroCall[D499, 0]@51..52#ROOT2024#
+    1.0#0:MacroCall[D499, 0]@61..64#ROOT2024#;#0:MacroCall[D499, 0]@64..65#ROOT2024#
+    (#0:MacroCall[D499, 0]@74..75#ROOT2024#(#0:MacroCall[D499, 0]@75..76#ROOT2024#1#0:MacroCall[D499, 0]@76..77#ROOT2024#,#0:MacroCall[D499, 0]@77..78#ROOT2024# )#0:MacroCall[D499, 0]@78..79#ROOT2024#,#0:MacroCall[D499, 0]@79..80#ROOT2024# )#0:MacroCall[D499, 0]@80..81#ROOT2024#.#0:MacroCall[D499, 0]@81..82#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#.#0:MacroCall[D499, 0]@82..85#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#;#0:MacroCall[D499, 0]@85..86#ROOT2024#
+    let#0:MacroCall[D499, 0]@95..98#ROOT2024# x#0:MacroCall[D499, 0]@99..100#ROOT2024# =#0:MacroCall[D499, 0]@101..102#ROOT2024# 1#0:MacroCall[D499, 0]@103..104#ROOT2024#;#0:MacroCall[D499, 0]@104..105#ROOT2024#
+}#0:MacroCall[D499, 0]@110..111#ROOT2024#
 
 
 "#]],
@@ -171,7 +171,7 @@
     }
 
     fn main(foo: ()) {
-        /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#ROOT2024#;
+        /* error: unresolved macro unresolved */"helloworld!"#0:Fn[B9C7, 0]@236..321#ROOT2024#;
     }
 }
 
@@ -197,7 +197,7 @@
 #[macro_use]
 mod foo;
 
-struct#1:1@59..65#14336# Foo#0:2@32..35#ROOT2024#(#1:1@70..71#14336#u32#0:2@41..44#ROOT2024#)#1:1@74..75#14336#;#1:1@75..76#14336#
+struct#1:MacroRules[E572, 0]@59..65#14336# Foo#0:MacroCall[BDD3, 0]@32..35#ROOT2024#(#1:MacroRules[E572, 0]@70..71#14336#u32#0:MacroCall[BDD3, 0]@41..44#ROOT2024#)#1:MacroRules[E572, 0]@74..75#14336#;#1:MacroRules[E572, 0]@75..76#14336#
 "#]],
     );
 }
@@ -2029,3 +2029,25 @@
     "#]],
     );
 }
+
+#[test]
+fn lifetime_repeat() {
+    check(
+        r#"
+macro_rules! m {
+    ($($x:expr)'a*) => (stringify!($($x)'b*));
+}
+fn f() {
+    let _ = m!(0 'a 1 'a 2);
+}
+    "#,
+        expect![[r#"
+macro_rules! m {
+    ($($x:expr)'a*) => (stringify!($($x)'b*));
+}
+fn f() {
+    let _ = stringify!(0 'b 1 'b 2);
+}
+    "#]],
+    );
+}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 2d289b7..2c94f0e 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
@@ -13,6 +13,8 @@
     ($(x),*) => ();
     ($(x)_*) => ();
     ($(x)i*) => ();
+    ($(x)'a*) => ();
+    ($(x)'_*) => ();
     ($($i:ident)*) => ($_);
     ($($true:ident)*) => ($true);
     ($($false:ident)*) => ($false);
@@ -28,6 +30,8 @@
     ($(x),*) => ();
     ($(x)_*) => ();
     ($(x)i*) => ();
+    ($(x)'a*) => ();
+    ($(x)'_*) => ();
     ($($i:ident)*) => ($_);
     ($($true:ident)*) => ($true);
     ($($false:ident)*) => ($false);
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 2cc3ca8..e2022c7 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -784,7 +784,7 @@
         }
     }
 }
-impl <> Data for &'amut G where G: Data {}
+impl <> Data for &'a mut G where G: Data {}
 "#]],
     );
 }
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index dc4334e..1c69b37 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -14,7 +14,7 @@
 mod mbe;
 mod proc_macros;
 
-use std::{iter, ops::Range, sync};
+use std::{any::TypeId, iter, ops::Range, sync};
 
 use base_db::RootQueryDb;
 use expect_test::Expect;
@@ -302,14 +302,15 @@
             (_, T!['{']) => " ",
             (T![;] | T!['{'] | T!['}'], _) => "\n",
             (_, T!['}']) => "\n",
-            (IDENT | LIFETIME_IDENT, IDENT | LIFETIME_IDENT) => " ",
-            _ if prev_kind.is_keyword(Edition::CURRENT)
-                && curr_kind.is_keyword(Edition::CURRENT) =>
+            _ if (prev_kind.is_any_identifier()
+                || prev_kind == LIFETIME_IDENT
+                || prev_kind.is_literal())
+                && (curr_kind.is_any_identifier()
+                    || curr_kind == LIFETIME_IDENT
+                    || curr_kind.is_literal()) =>
             {
                 " "
             }
-            (IDENT, _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
-            (_, IDENT) if prev_kind.is_keyword(Edition::CURRENT) => " ",
             (T![>], IDENT) => " ",
             (T![>], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
             (T![->], _) | (_, T![->]) => " ",
@@ -380,4 +381,8 @@
             panic!("got invalid macro input: {:?}", parse.errors());
         }
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index b2e1adc..d5ae6f8 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -181,9 +181,9 @@
     self.0. 1;
 }
 
-fn#0:1@45..47#ROOT2024# foo#0:1@48..51#ROOT2024#(#0:1@51..52#ROOT2024#&#0:1@52..53#ROOT2024#self#0:1@53..57#ROOT2024# )#0:1@57..58#ROOT2024# {#0:1@59..60#ROOT2024#
-    self#0:1@65..69#ROOT2024# .#0:1@69..70#ROOT2024#0#0:1@70..71#ROOT2024#.#0:1@71..72#ROOT2024#1#0:1@73..74#ROOT2024#;#0:1@74..75#ROOT2024#
-}#0:1@76..77#ROOT2024#"#]],
+fn#0:Fn[4D85, 0]@45..47#ROOT2024# foo#0:Fn[4D85, 0]@48..51#ROOT2024#(#0:Fn[4D85, 0]@51..52#ROOT2024#&#0:Fn[4D85, 0]@52..53#ROOT2024#self#0:Fn[4D85, 0]@53..57#ROOT2024# )#0:Fn[4D85, 0]@57..58#ROOT2024# {#0:Fn[4D85, 0]@59..60#ROOT2024#
+    self#0:Fn[4D85, 0]@65..69#ROOT2024# .#0:Fn[4D85, 0]@69..70#ROOT2024#0#0:Fn[4D85, 0]@70..71#ROOT2024#.#0:Fn[4D85, 0]@71..72#ROOT2024#1#0:Fn[4D85, 0]@73..74#ROOT2024#;#0:Fn[4D85, 0]@74..75#ROOT2024#
+}#0:Fn[4D85, 0]@76..77#ROOT2024#"#]],
     );
 }
 
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index f337f83..0837308 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -62,8 +62,8 @@
 
 use base_db::Crate;
 use hir_expand::{
-    EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, mod_path::ModPath,
-    name::Name, proc_macro::ProcMacroKind,
+    EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name,
+    proc_macro::ProcMacroKind,
 };
 use intern::Symbol;
 use itertools::Itertools;
@@ -80,7 +80,7 @@
     LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
     db::DefDatabase,
     item_scope::{BuiltinShadowMode, ItemScope},
-    item_tree::{ItemTreeId, Mod, TreeId},
+    item_tree::TreeId,
     nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode},
     per_ns::PerNs,
     visibility::{Visibility, VisibilityExplicitness},
@@ -171,12 +171,10 @@
     /// ExternCrateId being None implies it being imported from the general prelude import.
     macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>,
 
-    // FIXME: AstId's are fairly unstable
     /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
     /// attributes.
     // FIXME: Figure out a better way for the IDE layer to resolve these?
     derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
-    // FIXME: AstId's are fairly unstable
     /// A mapping from [`hir_expand::MacroDefId`] to [`crate::MacroId`].
     pub macro_def_to_macro_id: FxHashMap<ErasedAstId, MacroId>,
 
@@ -191,7 +189,7 @@
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct DefMapCrateData {
     /// Side table for resolving derive helpers.
-    exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
+    exported_derives: FxHashMap<MacroId, Box<[Name]>>,
     fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
 
     /// Custom attributes registered with `#![register_attr]`.
@@ -291,11 +289,11 @@
     File {
         is_mod_rs: bool,
         declaration: FileAstId<ast::Module>,
-        declaration_tree_id: ItemTreeId<Mod>,
+        declaration_tree_id: TreeId,
         definition: EditionedFileId,
     },
     Inline {
-        definition_tree_id: ItemTreeId<Mod>,
+        definition_tree_id: TreeId,
         definition: FileAstId<ast::Module>,
     },
     /// Pseudo-module introduced by a block scope (contains only inner items).
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs
index 86225d3..7aaa918 100644
--- a/crates/hir-def/src/nameres/assoc.rs
+++ b/crates/hir-def/src/nameres/assoc.rs
@@ -1,14 +1,28 @@
 //! Expansion of associated items
 
-use hir_expand::{AstId, InFile, Intern, Lookup, MacroCallKind, MacroDefKind, name::Name};
-use syntax::ast;
+use std::mem;
+
+use cfg::CfgOptions;
+use hir_expand::{
+    AstId, ExpandTo, HirFileId, InFile, Intern, Lookup, MacroCallKind, MacroDefKind,
+    mod_path::ModPath,
+    name::{AsName, Name},
+    span_map::SpanMap,
+};
+use intern::Interned;
+use span::AstIdMap;
+use syntax::{
+    AstNode,
+    ast::{self, HasModuleItem, HasName},
+};
+use thin_vec::ThinVec;
 use triomphe::Arc;
 
 use crate::{
     AssocItemId, AstIdWithPath, ConstLoc, FunctionId, FunctionLoc, ImplId, ItemContainerId,
     ItemLoc, MacroCallId, ModuleId, TraitId, TypeAliasId, TypeAliasLoc,
+    attr::Attrs,
     db::DefDatabase,
-    item_tree::{AssocItem, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId},
     macro_call_as_call_id,
     nameres::{
         DefMap, LocalDefMap, MacroSubNs,
@@ -20,9 +34,8 @@
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TraitItems {
     pub items: Box<[(Name, AssocItemId)]>,
-    // box it as the vec is usually empty anyways
-    // FIXME: AstIds are rather unstable...
-    pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+    // `ThinVec` as the vec is usually empty anyways
+    pub macro_calls: ThinVec<(AstId<ast::Item>, MacroCallId)>,
 }
 
 impl TraitItems {
@@ -35,12 +48,12 @@
         db: &dyn DefDatabase,
         tr: TraitId,
     ) -> (Arc<TraitItems>, DefDiagnostics) {
-        let ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
+        let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db);
 
-        let collector = AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr));
-        let item_tree = tree_id.item_tree(db);
-        let (items, macro_calls, diagnostics) =
-            collector.collect(&item_tree, tree_id.tree_id(), &item_tree[tree_id.value].items);
+        let collector =
+            AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr), ast_id.file_id);
+        let source = ast_id.with_value(collector.ast_id_map.get(ast_id.value)).to_node(db);
+        let (items, macro_calls, diagnostics) = collector.collect(source.assoc_item_list());
 
         (Arc::new(TraitItems { macro_calls, items }), DefDiagnostics::new(diagnostics))
     }
@@ -76,41 +89,36 @@
     }
 
     pub fn macro_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
-        self.macro_calls.iter().flat_map(|it| it.iter()).copied()
+        self.macro_calls.iter().copied()
     }
 }
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct ImplItems {
     pub items: Box<[(Name, AssocItemId)]>,
-    // box it as the vec is usually empty anyways
-    // FIXME: AstIds are rather unstable...
-    pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+    // `ThinVec` as the vec is usually empty anyways
+    pub macro_calls: ThinVec<(AstId<ast::Item>, MacroCallId)>,
+}
+
+#[salsa::tracked]
+impl ImplItems {
+    #[salsa::tracked(returns(ref))]
+    pub fn of(db: &dyn DefDatabase, id: ImplId) -> (ImplItems, DefDiagnostics) {
+        let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered();
+        let ItemLoc { container: module_id, id: ast_id } = id.lookup(db);
+
+        let collector =
+            AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id), ast_id.file_id);
+        let source = ast_id.with_value(collector.ast_id_map.get(ast_id.value)).to_node(db);
+        let (items, macro_calls, diagnostics) = collector.collect(source.assoc_item_list());
+
+        (ImplItems { items, macro_calls }, DefDiagnostics::new(diagnostics))
+    }
 }
 
 impl ImplItems {
-    #[inline]
-    pub(crate) fn impl_items_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplItems> {
-        db.impl_items_with_diagnostics(id).0
-    }
-
-    pub(crate) fn impl_items_with_diagnostics_query(
-        db: &dyn DefDatabase,
-        id: ImplId,
-    ) -> (Arc<ImplItems>, DefDiagnostics) {
-        let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered();
-        let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
-
-        let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id));
-        let item_tree = tree_id.item_tree(db);
-        let (items, macro_calls, diagnostics) =
-            collector.collect(&item_tree, tree_id.tree_id(), &item_tree[tree_id.value].items);
-
-        (Arc::new(ImplItems { items, macro_calls }), DefDiagnostics::new(diagnostics))
-    }
-
     pub fn macro_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
-        self.macro_calls.iter().flat_map(|it| it.iter()).copied()
+        self.macro_calls.iter().copied()
     }
 }
 
@@ -119,67 +127,73 @@
     module_id: ModuleId,
     def_map: &'a DefMap,
     local_def_map: &'a LocalDefMap,
+    ast_id_map: Arc<AstIdMap>,
+    span_map: SpanMap,
+    cfg_options: &'a CfgOptions,
+    file_id: HirFileId,
     diagnostics: Vec<DefDiagnostic>,
     container: ItemContainerId,
 
     depth: usize,
     items: Vec<(Name, AssocItemId)>,
-    macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
+    macro_calls: ThinVec<(AstId<ast::Item>, MacroCallId)>,
 }
 
 impl<'a> AssocItemCollector<'a> {
-    fn new(db: &'a dyn DefDatabase, module_id: ModuleId, container: ItemContainerId) -> Self {
+    fn new(
+        db: &'a dyn DefDatabase,
+        module_id: ModuleId,
+        container: ItemContainerId,
+        file_id: HirFileId,
+    ) -> Self {
         let (def_map, local_def_map) = module_id.local_def_map(db);
         Self {
             db,
             module_id,
             def_map,
             local_def_map,
+            ast_id_map: db.ast_id_map(file_id),
+            span_map: db.span_map(file_id),
+            cfg_options: module_id.krate.cfg_options(db),
+            file_id,
             container,
             items: Vec::new(),
 
             depth: 0,
-            macro_calls: Vec::new(),
+            macro_calls: ThinVec::new(),
             diagnostics: Vec::new(),
         }
     }
 
     fn collect(
         mut self,
-        item_tree: &ItemTree,
-        tree_id: TreeId,
-        assoc_items: &[AssocItem],
-    ) -> (
-        Box<[(Name, AssocItemId)]>,
-        Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
-        Vec<DefDiagnostic>,
-    ) {
-        self.items.reserve(assoc_items.len());
-        for &item in assoc_items {
-            self.collect_item(item_tree, tree_id, item);
+        item_list: Option<ast::AssocItemList>,
+    ) -> (Box<[(Name, AssocItemId)]>, ThinVec<(AstId<ast::Item>, MacroCallId)>, Vec<DefDiagnostic>)
+    {
+        if let Some(item_list) = item_list {
+            for item in item_list.assoc_items() {
+                self.collect_item(item);
+            }
         }
-        (
-            self.items.into_boxed_slice(),
-            if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
-            self.diagnostics,
-        )
+        self.macro_calls.shrink_to_fit();
+        (self.items.into_boxed_slice(), self.macro_calls, self.diagnostics)
     }
 
-    fn collect_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem) {
-        let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
-        if !attrs.is_cfg_enabled(self.module_id.krate.cfg_options(self.db)) {
+    fn collect_item(&mut self, item: ast::AssocItem) {
+        let ast_id = self.ast_id_map.ast_id(&item);
+        let attrs = Attrs::new(self.db, &item, self.span_map.as_ref(), self.cfg_options);
+        if let Err(cfg) = attrs.is_cfg_enabled(self.cfg_options) {
             self.diagnostics.push(DefDiagnostic::unconfigured_code(
                 self.module_id.local_id,
-                tree_id,
-                ModItem::from(item).into(),
-                attrs.cfg().unwrap(),
-                self.module_id.krate.cfg_options(self.db).clone(),
+                InFile::new(self.file_id, ast_id.erase()),
+                cfg,
+                self.cfg_options.clone(),
             ));
             return;
         }
+        let ast_id = InFile::new(self.file_id, ast_id.upcast());
 
         'attrs: for attr in &*attrs {
-            let ast_id = AstId::new(tree_id.file_id(), item.ast_id(item_tree).upcast());
             let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
 
             match self.def_map.resolve_attr_macro(
@@ -223,34 +237,51 @@
             }
         }
 
-        self.record_item(item_tree, tree_id, item);
+        self.record_item(item);
     }
 
-    fn record_item(&mut self, item_tree: &ItemTree, tree_id: TreeId, item: AssocItem) {
+    fn record_item(&mut self, item: ast::AssocItem) {
         match item {
-            AssocItem::Function(id) => {
-                let item = &item_tree[id];
+            ast::AssocItem::Fn(function) => {
+                let Some(name) = function.name() else { return };
+                let ast_id = self.ast_id_map.ast_id(&function);
+                let def = FunctionLoc {
+                    container: self.container,
+                    id: InFile::new(self.file_id, ast_id),
+                }
+                .intern(self.db);
+                self.items.push((name.as_name(), def.into()));
+            }
+            ast::AssocItem::TypeAlias(type_alias) => {
+                let Some(name) = type_alias.name() else { return };
+                let ast_id = self.ast_id_map.ast_id(&type_alias);
+                let def = TypeAliasLoc {
+                    container: self.container,
+                    id: InFile::new(self.file_id, ast_id),
+                }
+                .intern(self.db);
+                self.items.push((name.as_name(), def.into()));
+            }
+            ast::AssocItem::Const(konst) => {
+                let Some(name) = konst.name() else { return };
+                let ast_id = self.ast_id_map.ast_id(&konst);
                 let def =
-                    FunctionLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
+                    ConstLoc { container: self.container, id: InFile::new(self.file_id, ast_id) }
                         .intern(self.db);
-                self.items.push((item.name.clone(), def.into()));
+                self.items.push((name.as_name(), def.into()));
             }
-            AssocItem::TypeAlias(id) => {
-                let item = &item_tree[id];
-                let def =
-                    TypeAliasLoc { container: self.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: self.container, id: ItemTreeId::new(tree_id, id) }
-                    .intern(self.db);
-                self.items.push((name, def.into()));
-            }
-            AssocItem::MacroCall(call) => {
-                let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
+            ast::AssocItem::MacroCall(call) => {
+                let ast_id = self.ast_id_map.ast_id(&call);
+                let ast_id = InFile::new(self.file_id, ast_id);
+                let Some(path) = call.path() else { return };
+                let range = path.syntax().text_range();
+                let Some(path) = ModPath::from_src(self.db, path, &mut |range| {
+                    self.span_map.span_for_range(range).ctx
+                }) else {
+                    return;
+                };
+                let path = Interned::new(path);
+                let ctxt = self.span_map.span_for_range(range).ctx;
 
                 let resolver = |path: &_| {
                     self.def_map
@@ -268,10 +299,10 @@
                 };
                 match macro_call_as_call_id(
                     self.db,
-                    InFile::new(tree_id.file_id(), ast_id),
-                    path,
+                    ast_id,
+                    &path,
                     ctxt,
-                    expand_to,
+                    ExpandTo::Items,
                     self.module_id.krate(),
                     resolver,
                     &mut |ptr, call_id| {
@@ -281,8 +312,7 @@
                     // FIXME: Expansion error?
                     Ok(call_id) => match call_id.value {
                         Some(call_id) => {
-                            self.macro_calls
-                                .push((InFile::new(tree_id.file_id(), ast_id.upcast()), call_id));
+                            self.macro_calls.push((ast_id.upcast(), call_id));
                             self.collect_macro_items(call_id);
                         }
                         None => (),
@@ -291,11 +321,11 @@
                         self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
                             self.module_id.local_id,
                             MacroCallKind::FnLike {
-                                ast_id: InFile::new(tree_id.file_id(), ast_id),
-                                expand_to,
+                                ast_id,
+                                expand_to: ExpandTo::Items,
                                 eager: None,
                             },
-                            Clone::clone(path),
+                            (*path).clone(),
                         ));
                     }
                 }
@@ -308,13 +338,29 @@
             tracing::warn!("macro expansion is too deep");
             return;
         }
-        let tree_id = TreeId::new(macro_call_id.into(), None);
-        let item_tree = self.db.file_item_tree(macro_call_id.into());
 
+        let (syntax, span_map) = self.db.parse_macro_expansion(macro_call_id).value;
+        let old_file_id = mem::replace(&mut self.file_id, macro_call_id.into());
+        let old_ast_id_map = mem::replace(&mut self.ast_id_map, self.db.ast_id_map(self.file_id));
+        let old_span_map = mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(span_map));
         self.depth += 1;
-        for item in item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item) {
-            self.collect_item(&item_tree, tree_id, item);
+
+        let items = ast::MacroItems::cast(syntax.syntax_node()).expect("not `MacroItems`");
+        for item in items.items() {
+            let item = match item {
+                ast::Item::Fn(it) => ast::AssocItem::from(it),
+                ast::Item::Const(it) => it.into(),
+                ast::Item::TypeAlias(it) => it.into(),
+                ast::Item::MacroCall(it) => it.into(),
+                // FIXME: Should error on disallowed item kinds.
+                _ => continue,
+            };
+            self.collect_item(item);
         }
+
         self.depth -= 1;
+        self.file_id = old_file_id;
+        self.ast_id_map = old_ast_id_map;
+        self.span_map = old_span_map;
     }
 }
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 350c97c..78fdc27 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -9,8 +9,8 @@
 use cfg::{CfgAtom, CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
-    EditionedFileId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
-    MacroDefKind,
+    EditionedFileId, ErasedAstId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
+    MacroDefId, MacroDefKind,
     attrs::{Attr, AttrId},
     builtin::{find_builtin_attr, find_builtin_derive, find_builtin_macro},
     mod_path::{ModPath, PathKind},
@@ -35,9 +35,8 @@
     db::DefDatabase,
     item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
     item_tree::{
-        self, AttrOwner, FieldsShape, FileItemTreeId, ImportAlias, ImportKind, ItemTree,
-        ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
-        UseTreeKind,
+        self, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId, Macro2, MacroCall,
+        MacroRules, Mod, ModItemId, ModKind, TreeId,
     },
     macro_call_as_call_id,
     nameres::{
@@ -154,14 +153,14 @@
 impl Import {
     fn from_use(
         tree: &ItemTree,
-        item_tree_id: ItemTreeId<item_tree::Use>,
+        item: FileAstId<ast::Use>,
         id: UseId,
         is_prelude: bool,
         mut cb: impl FnMut(Self),
     ) {
-        let it = &tree[item_tree_id.value];
+        let it = &tree[item];
         let visibility = &tree[it.visibility];
-        it.use_tree.expand(|idx, path, kind, alias| {
+        it.expand(|idx, path, kind, alias| {
             cb(Self {
                 path,
                 alias,
@@ -181,15 +180,15 @@
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
-struct MacroDirective {
+struct MacroDirective<'db> {
     module_id: LocalModuleId,
     depth: usize,
-    kind: MacroDirectiveKind,
+    kind: MacroDirectiveKind<'db>,
     container: ItemContainerId,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
-enum MacroDirectiveKind {
+enum MacroDirectiveKind<'db> {
     FnLike {
         ast_id: AstIdWithPath<ast::MacroCall>,
         expand_to: ExpandTo,
@@ -206,30 +205,31 @@
     Attr {
         ast_id: AstIdWithPath<ast::Item>,
         attr: Attr,
-        mod_item: ModItem,
+        mod_item: ModItemId,
         /* is this needed? */ tree: TreeId,
+        item_tree: &'db ItemTree,
     },
 }
 
 /// Walks the tree of module recursively
-struct DefCollector<'a> {
-    db: &'a dyn DefDatabase,
+struct DefCollector<'db> {
+    db: &'db dyn DefDatabase,
     def_map: DefMap,
     local_def_map: LocalDefMap,
     /// Set only in case of blocks.
-    crate_local_def_map: Option<&'a LocalDefMap>,
+    crate_local_def_map: Option<&'db LocalDefMap>,
     // The dependencies of the current crate, including optional deps like `test`.
     deps: FxHashMap<Name, BuiltDependency>,
     glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
     unresolved_imports: Vec<ImportDirective>,
     indeterminate_imports: Vec<(ImportDirective, PerNs)>,
-    unresolved_macros: Vec<MacroDirective>,
+    unresolved_macros: Vec<MacroDirective<'db>>,
     // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
     // resolve. When we emit diagnostics for unresolved imports, we only do so if the import
     // doesn't start with an unresolved crate's name.
     unresolved_extern_crates: FxHashSet<Name>,
     mod_dirs: FxHashMap<LocalModuleId, ModDir>,
-    cfg_options: &'a CfgOptions,
+    cfg_options: &'db CfgOptions,
     /// List of procedural macros defined by this crate. This is read from the dynamic library
     /// 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
@@ -244,10 +244,10 @@
     /// This also stores the attributes to skip when we resolve derive helpers and non-macro
     /// non-builtin attributes in general.
     // FIXME: There has to be a better way to do this
-    skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
+    skip_attrs: FxHashMap<InFile<FileAstId<ast::Item>>, AttrId>,
 }
 
-impl DefCollector<'_> {
+impl<'db> DefCollector<'db> {
     fn seed_with_top_level(&mut self) {
         let _p = tracing::info_span!("seed_with_top_level").entered();
 
@@ -355,7 +355,7 @@
             macro_depth: 0,
             module_id: DefMap::ROOT,
             tree_id: TreeId::new(file_id.into(), None),
-            item_tree: &item_tree,
+            item_tree,
             mod_dir: ModDir::root(),
         }
         .collect_in_top_module(item_tree.top_level_items());
@@ -376,7 +376,7 @@
                 macro_depth: 0,
                 module_id: DefMap::ROOT,
                 tree_id,
-                item_tree: &item_tree,
+                item_tree,
                 mod_dir: ModDir::root(),
             }
             .collect_in_top_module(item_tree.top_level_items());
@@ -437,9 +437,8 @@
             // 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 module_id = self.def_map.module_id(DefMap::ROOT);
             let root = &mut self.def_map.modules[DefMap::ROOT];
-            root.scope.censor_non_proc_macros(module_id);
+            root.scope.censor_non_proc_macros(self.def_map.krate);
         }
     }
 
@@ -459,7 +458,7 @@
             self.unresolved_macros.iter().enumerate().find_map(|(idx, directive)| match &directive
                 .kind
             {
-                MacroDirectiveKind::Attr { ast_id, mod_item, attr, tree } => {
+                MacroDirectiveKind::Attr { ast_id, mod_item, attr, tree, item_tree } => {
                     self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
                         directive.module_id,
                         MacroCallKind::Attr {
@@ -470,16 +469,22 @@
                         attr.path().clone(),
                     ));
 
-                    self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id);
+                    self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), attr.id);
 
-                    Some((idx, directive, *mod_item, *tree))
+                    Some((idx, directive, *mod_item, *tree, *item_tree))
                 }
                 _ => None,
             });
 
         match unresolved_attr {
-            Some((pos, &MacroDirective { module_id, depth, container, .. }, mod_item, tree_id)) => {
-                let item_tree = &tree_id.item_tree(self.db);
+            Some((
+                pos,
+                &MacroDirective { module_id, depth, container, .. },
+                mod_item,
+                tree_id,
+                item_tree,
+            )) => {
+                // FIXME: Remove this clone
                 let mod_dir = self.mod_dirs[&module_id].clone();
                 ModCollector {
                     def_collector: self,
@@ -576,13 +581,7 @@
     /// use a dummy expander that always errors. This comes with the drawback of macros potentially
     /// going out of sync with what the build system sees (since we resolve using VFS state, but
     /// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
-    fn export_proc_macro(
-        &mut self,
-        def: ProcMacroDef,
-        id: ItemTreeId<item_tree::Function>,
-        ast_id: AstId<ast::Fn>,
-        fn_id: FunctionId,
-    ) {
+    fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>, fn_id: FunctionId) {
         let kind = def.kind.to_basedb_kind();
         let (expander, kind) = match self.proc_macros.iter().find(|(n, _, _)| n == &def.name) {
             Some(_)
@@ -598,7 +597,7 @@
 
         let proc_macro_id = ProcMacroLoc {
             container: self.def_map.crate_root(),
-            id,
+            id: ast_id,
             expander,
             kind,
             edition: self.def_map.data.edition,
@@ -609,7 +608,7 @@
         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::Derive { helpers } = def.kind {
-            crate_data.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers);
+            crate_data.exported_derives.insert(proc_macro_id.into(), helpers);
         }
         crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
     }
@@ -887,9 +886,31 @@
                 let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree });
                 tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
 
+                // `extern crate crate_name` things can be re-exported as `pub use crate_name`.
+                // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name`
+                // or `pub use ::crate_name`.
+                //
+                // This has been historically allowed, but may be not allowed in future
+                // https://github.com/rust-lang/rust/issues/127909
+                if let Some(def) = def.types.as_mut() {
+                    let is_extern_crate_reimport_without_prefix = || {
+                        let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else {
+                            return false;
+                        };
+                        if kind == ImportKind::Glob {
+                            return false;
+                        }
+                        matches!(import.path.kind, PathKind::Plain | PathKind::SELF)
+                            && import.path.segments().len() < 2
+                    };
+                    if is_extern_crate_reimport_without_prefix() {
+                        def.vis = vis;
+                    }
+                }
+
                 self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
             }
-            ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => {
+            ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree, .. } => {
                 tracing::debug!("glob import: {:?}", import);
                 let glob = GlobId { use_: id, idx: use_tree };
                 match def.take_types() {
@@ -973,12 +994,11 @@
                     Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
                         cov_mark::hit!(glob_enum);
                         // glob import from enum => just import all the variants
-                        let resolutions = self
-                            .db
-                            .enum_variants(e)
+                        let resolutions = e
+                            .enum_variants(self.db)
                             .variants
                             .iter()
-                            .map(|&(variant, ref name)| {
+                            .map(|&(variant, ref name, _)| {
                                 let res = PerNs::both(variant.into(), variant.into(), vis, None);
                                 (Some(name.clone()), res)
                             })
@@ -1150,33 +1170,8 @@
         vis: Visibility,
         def_import_type: Option<ImportOrExternCrate>,
     ) -> bool {
-        // `extern crate crate_name` things can be re-exported as `pub use crate_name`.
-        // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name`
-        // or `pub use ::crate_name`.
-        //
-        // This has been historically allowed, but may be not allowed in future
-        // https://github.com/rust-lang/rust/issues/127909
         if let Some(def) = defs.types.as_mut() {
-            let is_extern_crate_reimport_without_prefix = || {
-                let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else {
-                    return false;
-                };
-                let Some(ImportOrExternCrate::Import(id)) = def_import_type else {
-                    return false;
-                };
-                let use_id = id.use_.lookup(self.db).id;
-                let item_tree = use_id.item_tree(self.db);
-                let use_kind = item_tree[use_id.value].use_tree.kind();
-                let UseTreeKind::Single { path, .. } = use_kind else {
-                    return false;
-                };
-                path.segments().len() < 2
-            };
-            if is_extern_crate_reimport_without_prefix() {
-                def.vis = vis;
-            } else {
-                def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
-            }
+            def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
         }
         if let Some(def) = defs.values.as_mut() {
             def.vis = def.vis.min(vis, &self.def_map).unwrap_or(vis);
@@ -1259,7 +1254,7 @@
     fn resolve_macros(&mut self) -> ReachedFixedPoint {
         let mut macros = mem::take(&mut self.unresolved_macros);
         let mut resolved = Vec::new();
-        let mut push_resolved = |directive: &MacroDirective, call_id| {
+        let mut push_resolved = |directive: &MacroDirective<'_>, call_id| {
             resolved.push((directive.module_id, directive.depth, directive.container, call_id));
         };
 
@@ -1272,7 +1267,7 @@
         let mut eager_callback_buffer = vec![];
         let mut res = ReachedFixedPoint::Yes;
         // Retain unresolved macros after this round of resolution.
-        let mut retain = |directive: &MacroDirective| {
+        let mut retain = |directive: &MacroDirective<'db>| {
             let subns = match &directive.kind {
                 MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
                 MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
@@ -1349,7 +1344,7 @@
                         // Record its helper attributes.
                         if def_id.krate != self.def_map.krate {
                             let def_map = crate_def_map(self.db, def_id.krate);
-                            if let Some(helpers) = def_map.data.exported_derives.get(&def_id) {
+                            if let Some(helpers) = def_map.data.exported_derives.get(&macro_id) {
                                 self.def_map
                                     .derive_helpers_in_scope
                                     .entry(ast_id.ast_id.map(|it| it.upcast()))
@@ -1367,22 +1362,29 @@
                         return Resolved::Yes;
                     }
                 }
-                MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr, tree } => {
+                MacroDirectiveKind::Attr {
+                    ast_id: file_ast_id,
+                    mod_item,
+                    attr,
+                    tree,
+                    item_tree,
+                } => {
                     let &AstIdWithPath { ast_id, ref path } = file_ast_id;
                     let file_id = ast_id.file_id;
 
                     let mut recollect_without = |collector: &mut Self| {
                         // Remove the original directive since we resolved it.
                         let mod_dir = collector.mod_dirs[&directive.module_id].clone();
-                        collector.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
+                        collector
+                            .skip_attrs
+                            .insert(InFile::new(file_id, mod_item.ast_id()), attr.id);
 
-                        let item_tree = tree.item_tree(self.db);
                         ModCollector {
                             def_collector: collector,
                             macro_depth: directive.depth,
                             module_id: directive.module_id,
                             tree_id: *tree,
-                            item_tree: &item_tree,
+                            item_tree,
                             mod_dir,
                         }
                         .collect(&[*mod_item], directive.container);
@@ -1435,11 +1437,10 @@
                         // normal (as that would just be an identity expansion with extra output)
                         // Instead we treat derive attributes special and apply them separately.
 
-                        let item_tree = tree.item_tree(self.db);
                         let ast_adt_id: FileAstId<ast::Adt> = match *mod_item {
-                            ModItem::Struct(strukt) => item_tree[strukt].ast_id().upcast(),
-                            ModItem::Union(union) => item_tree[union].ast_id().upcast(),
-                            ModItem::Enum(enum_) => item_tree[enum_].ast_id().upcast(),
+                            ModItemId::Struct(ast_id) => ast_id.upcast(),
+                            ModItemId::Union(ast_id) => ast_id.upcast(),
+                            ModItemId::Enum(ast_id) => ast_id.upcast(),
                             _ => {
                                 let diag = DefDiagnostic::invalid_derive_target(
                                     directive.module_id,
@@ -1571,7 +1572,7 @@
             macro_depth: depth,
             tree_id: TreeId::new(file_id, None),
             module_id,
-            item_tree: &item_tree,
+            item_tree,
             mod_dir,
         }
         .collect(item_tree.top_level_items(), container);
@@ -1672,22 +1673,22 @@
 }
 
 /// Walks a single module, populating defs, imports and macros
-struct ModCollector<'a, 'b> {
-    def_collector: &'a mut DefCollector<'b>,
+struct ModCollector<'a, 'db> {
+    def_collector: &'a mut DefCollector<'db>,
     macro_depth: usize,
     module_id: LocalModuleId,
     tree_id: TreeId,
-    item_tree: &'a ItemTree,
+    item_tree: &'db ItemTree,
     mod_dir: ModDir,
 }
 
 impl ModCollector<'_, '_> {
-    fn collect_in_top_module(&mut self, items: &[ModItem]) {
+    fn collect_in_top_module(&mut self, items: &[ModItemId]) {
         let module = self.def_collector.def_map.module_id(self.module_id);
         self.collect(items, module.into())
     }
 
-    fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
+    fn collect(&mut self, items: &[ModItemId], container: ItemContainerId) {
         let krate = self.def_collector.def_map.krate;
         let is_crate_root =
             self.module_id == DefMap::ROOT && self.def_collector.def_map.block.is_none();
@@ -1726,11 +1727,12 @@
                 .unwrap_or(Visibility::Public)
         };
 
-        let mut process_mod_item = |item: ModItem| {
-            let attrs = self.item_tree.attrs(db, krate, item.into());
+        let mut process_mod_item = |item: ModItemId| {
+            let attrs = self.item_tree.attrs(db, krate, item.ast_id());
             if let Some(cfg) = attrs.cfg() {
                 if !self.is_cfg_enabled(&cfg) {
-                    self.emit_unconfigured_diagnostic(self.tree_id, item.into(), &cfg);
+                    let ast_id = item.ast_id().erase();
+                    self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg);
                     return;
                 }
             }
@@ -1747,39 +1749,31 @@
                 self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map);
 
             match item {
-                ModItem::Mod(m) => self.collect_module(m, &attrs),
-                ModItem::Use(item_tree_id) => {
-                    let id = UseLoc {
-                        container: module,
-                        id: ItemTreeId::new(self.tree_id, item_tree_id),
-                    }
-                    .intern(db);
+                ModItemId::Mod(m) => self.collect_module(m, &attrs),
+                ModItemId::Use(item_tree_id) => {
+                    let id =
+                        UseLoc { container: module, id: InFile::new(self.file_id(), item_tree_id) }
+                            .intern(db);
                     let is_prelude = attrs.by_key(sym::prelude_import).exists();
-                    Import::from_use(
-                        self.item_tree,
-                        ItemTreeId::new(self.tree_id, item_tree_id),
-                        id,
-                        is_prelude,
-                        |import| {
-                            self.def_collector.unresolved_imports.push(ImportDirective {
-                                module_id: self.module_id,
-                                import,
-                                status: PartialResolvedImport::Unresolved,
-                            });
-                        },
-                    )
+                    Import::from_use(self.item_tree, item_tree_id, id, is_prelude, |import| {
+                        self.def_collector.unresolved_imports.push(ImportDirective {
+                            module_id: self.module_id,
+                            import,
+                            status: PartialResolvedImport::Unresolved,
+                        });
+                    })
                 }
-                ModItem::ExternCrate(item_tree_id) => {
+                ModItemId::ExternCrate(item_tree_id) => {
+                    let item_tree::ExternCrate { name, visibility, alias } =
+                        &self.item_tree[item_tree_id];
+
                     let id = ExternCrateLoc {
                         container: module,
-                        id: ItemTreeId::new(self.tree_id, item_tree_id),
+                        id: InFile::new(self.tree_id.file_id(), item_tree_id),
                     }
                     .intern(db);
                     def_map.modules[self.module_id].scope.define_extern_crate_decl(id);
 
-                    let item_tree::ExternCrate { name, visibility, alias, ast_id } =
-                        &self.item_tree[item_tree_id];
-
                     let is_self = *name == sym::self_;
                     let resolved = if is_self {
                         cov_mark::hit!(extern_crate_self_as);
@@ -1838,15 +1832,15 @@
                         self.def_collector.def_map.diagnostics.push(
                             DefDiagnostic::unresolved_extern_crate(
                                 module_id,
-                                InFile::new(self.file_id(), *ast_id),
+                                InFile::new(self.file_id(), item_tree_id),
                             ),
                         );
                     }
                 }
-                ModItem::ExternBlock(block) => {
+                ModItemId::ExternBlock(block) => {
                     let extern_block_id = ExternBlockLoc {
                         container: module,
-                        id: ItemTreeId::new(self.tree_id, block),
+                        id: InFile::new(self.file_id(), block),
                     }
                     .intern(db);
                     self.def_collector.def_map.modules[self.module_id]
@@ -1857,19 +1851,20 @@
                         ItemContainerId::ExternBlockId(extern_block_id),
                     )
                 }
-                ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac], container),
-                ModItem::MacroRules(id) => self.collect_macro_rules(id, module),
-                ModItem::Macro2(id) => self.collect_macro_def(id, module),
-                ModItem::Impl(imp) => {
+                ModItemId::MacroCall(mac) => self.collect_macro_call(mac, container),
+                ModItemId::MacroRules(id) => self.collect_macro_rules(id, module),
+                ModItemId::Macro2(id) => self.collect_macro_def(id, module),
+                ModItemId::Impl(imp) => {
                     let impl_id =
-                        ImplLoc { container: module, id: ItemTreeId::new(self.tree_id, imp) }
+                        ImplLoc { container: module, id: InFile::new(self.file_id(), imp) }
                             .intern(db);
                     self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
                 }
-                ModItem::Function(id) => {
+                ModItemId::Function(id) => {
                     let it = &self.item_tree[id];
                     let fn_id =
-                        FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
+                        FunctionLoc { container, id: InFile::new(self.tree_id.file_id(), id) }
+                            .intern(db);
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
 
@@ -1880,8 +1875,7 @@
                         if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
                             self.def_collector.export_proc_macro(
                                 proc_macro,
-                                ItemTreeId::new(self.tree_id, id),
-                                InFile::new(self.file_id(), self.item_tree[id].ast_id()),
+                                InFile::new(self.file_id(), id),
                                 fn_id,
                             );
                         }
@@ -1889,13 +1883,13 @@
 
                     update_def(self.def_collector, fn_id.into(), &it.name, vis, false);
                 }
-                ModItem::Struct(id) => {
+                ModItemId::Struct(id) => {
                     let it = &self.item_tree[id];
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        StructLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                        StructLoc { container: module, id: InFile::new(self.file_id(), id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -1903,13 +1897,13 @@
                         !matches!(it.shape, FieldsShape::Record),
                     );
                 }
-                ModItem::Union(id) => {
+                ModItemId::Union(id) => {
                     let it = &self.item_tree[id];
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        UnionLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                        UnionLoc { container: module, id: InFile::new(self.file_id(), id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -1917,19 +1911,20 @@
                         false,
                     );
                 }
-                ModItem::Enum(id) => {
+                ModItemId::Enum(id) => {
                     let it = &self.item_tree[id];
                     let enum_ =
-                        EnumLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                        EnumLoc { container: module, id: InFile::new(self.tree_id.file_id(), id) }
                             .intern(db);
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(self.def_collector, enum_.into(), &it.name, vis, false);
                 }
-                ModItem::Const(id) => {
+                ModItemId::Const(id) => {
                     let it = &self.item_tree[id];
                     let const_id =
-                        ConstLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
+                        ConstLoc { container, id: InFile::new(self.tree_id.file_id(), id) }
+                            .intern(db);
 
                     match &it.name {
                         Some(name) => {
@@ -1945,13 +1940,13 @@
                         }
                     }
                 }
-                ModItem::Static(id) => {
+                ModItemId::Static(id) => {
                     let it = &self.item_tree[id];
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        StaticLoc { container, id: ItemTreeId::new(self.tree_id, id) }
+                        StaticLoc { container, id: InFile::new(self.file_id(), id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -1959,13 +1954,13 @@
                         false,
                     );
                 }
-                ModItem::Trait(id) => {
+                ModItemId::Trait(id) => {
                     let it = &self.item_tree[id];
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        TraitLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                        TraitLoc { container: module, id: InFile::new(self.file_id(), id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -1973,13 +1968,13 @@
                         false,
                     );
                 }
-                ModItem::TraitAlias(id) => {
+                ModItemId::TraitAlias(id) => {
                     let it = &self.item_tree[id];
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        TraitAliasLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                        TraitAliasLoc { container: module, id: InFile::new(self.file_id(), id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -1987,13 +1982,13 @@
                         false,
                     );
                 }
-                ModItem::TypeAlias(id) => {
+                ModItemId::TypeAlias(id) => {
                     let it = &self.item_tree[id];
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        TypeAliasLoc { container, id: ItemTreeId::new(self.tree_id, id) }
+                        TypeAliasLoc { container, id: InFile::new(self.file_id(), id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -2010,12 +2005,12 @@
         if is_crate_root {
             items
                 .iter()
-                .filter(|it| matches!(it, ModItem::ExternCrate(..)))
+                .filter(|it| matches!(it, ModItemId::ExternCrate(..)))
                 .copied()
                 .for_each(&mut process_mod_item);
             items
                 .iter()
-                .filter(|it| !matches!(it, ModItem::ExternCrate(..)))
+                .filter(|it| !matches!(it, ModItemId::ExternCrate(..)))
                 .copied()
                 .for_each(process_mod_item);
         } else {
@@ -2056,19 +2051,18 @@
         );
     }
 
-    fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
+    fn collect_module(&mut self, module_ast_id: ItemTreeAstId<Mod>, attrs: &Attrs) {
         let path_attr = attrs.by_key(sym::path).string_value_unescape();
         let is_macro_use = attrs.by_key(sym::macro_use).exists();
-        let module = &self.item_tree[module_id];
+        let module = &self.item_tree[module_ast_id];
         match &module.kind {
             // inline module, just recurse
             ModKind::Inline { items } => {
                 let module_id = self.push_child_module(
                     module.name.clone(),
-                    module.ast_id,
+                    module_ast_id,
                     None,
                     &self.item_tree[module.visibility],
-                    module_id,
                 );
 
                 let Some(mod_dir) =
@@ -2091,7 +2085,7 @@
             }
             // out of line module, resolve, parse and recurse
             ModKind::Outline => {
-                let ast_id = AstId::new(self.file_id(), module.ast_id);
+                let ast_id = AstId::new(self.file_id(), module_ast_id);
                 let db = self.def_collector.db;
                 match self.mod_dir.resolve_declaration(
                     db,
@@ -2110,8 +2104,7 @@
                         match is_enabled {
                             Err(cfg) => {
                                 self.emit_unconfigured_diagnostic(
-                                    self.tree_id,
-                                    AttrOwner::ModItem(module_id.into()),
+                                    InFile::new(self.file_id(), module_ast_id.erase()),
                                     &cfg,
                                 );
                             }
@@ -2121,14 +2114,13 @@
                                     ast_id.value,
                                     Some((file_id, is_mod_rs)),
                                     &self.item_tree[module.visibility],
-                                    module_id,
                                 );
                                 ModCollector {
                                     def_collector: self.def_collector,
                                     macro_depth: self.macro_depth,
                                     module_id,
                                     tree_id: TreeId::new(file_id.into(), None),
-                                    item_tree: &item_tree,
+                                    item_tree,
                                     mod_dir,
                                 }
                                 .collect_in_top_module(item_tree.top_level_items());
@@ -2149,7 +2141,6 @@
                             ast_id.value,
                             None,
                             &self.item_tree[module.visibility],
-                            module_id,
                         );
                         self.def_collector.def_map.diagnostics.push(
                             DefDiagnostic::unresolved_module(self.module_id, ast_id, candidates),
@@ -2166,7 +2157,6 @@
         declaration: FileAstId<ast::Module>,
         definition: Option<(EditionedFileId, bool)>,
         visibility: &crate::visibility::RawVisibility,
-        mod_tree_id: FileItemTreeId<Mod>,
     ) -> LocalModuleId {
         let def_map = &mut self.def_collector.def_map;
         let vis = def_map
@@ -2179,15 +2169,14 @@
             )
             .unwrap_or(Visibility::Public);
         let origin = match definition {
-            None => ModuleOrigin::Inline {
-                definition: declaration,
-                definition_tree_id: ItemTreeId::new(self.tree_id, mod_tree_id),
-            },
+            None => {
+                ModuleOrigin::Inline { definition: declaration, definition_tree_id: self.tree_id }
+            }
             Some((definition, is_mod_rs)) => ModuleOrigin::File {
                 declaration,
                 definition,
                 is_mod_rs,
-                declaration_tree_id: ItemTreeId::new(self.tree_id, mod_tree_id),
+                declaration_tree_id: self.tree_id,
             },
         };
 
@@ -2228,11 +2217,14 @@
     fn resolve_attributes(
         &mut self,
         attrs: &Attrs,
-        mod_item: ModItem,
+        mod_item: ModItemId,
         container: ItemContainerId,
     ) -> Result<(), ()> {
-        let mut ignore_up_to =
-            self.def_collector.skip_attrs.get(&InFile::new(self.file_id(), mod_item)).copied();
+        let mut ignore_up_to = self
+            .def_collector
+            .skip_attrs
+            .get(&InFile::new(self.file_id(), mod_item.ast_id()))
+            .copied();
         let iter = attrs
             .iter()
             .dedup_by(|a, b| {
@@ -2262,11 +2254,7 @@
                 attr.path.display(self.def_collector.db, Edition::LATEST)
             );
 
-            let ast_id = AstIdWithPath::new(
-                self.file_id(),
-                mod_item.ast_id(self.item_tree),
-                attr.path.clone(),
-            );
+            let ast_id = AstIdWithPath::new(self.file_id(), mod_item.ast_id(), attr.path.clone());
             self.def_collector.unresolved_macros.push(MacroDirective {
                 module_id: self.module_id,
                 depth: self.macro_depth + 1,
@@ -2275,6 +2263,7 @@
                     attr: attr.clone(),
                     mod_item,
                     tree: self.tree_id,
+                    item_tree: self.item_tree,
                 },
                 container,
             });
@@ -2285,11 +2274,11 @@
         Ok(())
     }
 
-    fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>, module: ModuleId) {
+    fn collect_macro_rules(&mut self, ast_id: ItemTreeAstId<MacroRules>, module: ModuleId) {
         let krate = self.def_collector.def_map.krate;
-        let mac = &self.item_tree[id];
-        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
-        let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
+        let mac = &self.item_tree[ast_id];
+        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast());
+        let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
 
         let export_attr = || attrs.by_key(sym::macro_export);
 
@@ -2336,7 +2325,7 @@
                     self.def_collector
                         .def_map
                         .diagnostics
-                        .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, ast_id));
+                        .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id));
                     return;
                 }
             }
@@ -2352,16 +2341,13 @@
 
         let macro_id = MacroRulesLoc {
             container: module,
-            id: ItemTreeId::new(self.tree_id, id),
+            id: InFile::new(self.file_id(), ast_id),
             flags,
             expander,
             edition: self.def_collector.def_map.data.edition,
         }
         .intern(self.def_collector.db);
-        self.def_collector.def_map.macro_def_to_macro_id.insert(
-            InFile::new(self.file_id(), self.item_tree[id].ast_id()).erase(),
-            macro_id.into(),
-        );
+        self.def_collector.def_map.macro_def_to_macro_id.insert(f_ast_id.erase(), macro_id.into());
         self.def_collector.define_macro_rules(
             self.module_id,
             mac.name.clone(),
@@ -2370,14 +2356,14 @@
         );
     }
 
-    fn collect_macro_def(&mut self, id: FileItemTreeId<Macro2>, module: ModuleId) {
+    fn collect_macro_def(&mut self, ast_id: ItemTreeAstId<Macro2>, module: ModuleId) {
         let krate = self.def_collector.def_map.krate;
-        let mac = &self.item_tree[id];
-        let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
+        let mac = &self.item_tree[ast_id];
+        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ast_id.upcast());
+        let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
 
         // Case 1: builtin macros
         let mut helpers_opt = None;
-        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
         let expander = if attrs.by_key(sym::rustc_builtin_macro).exists() {
             if let Some(expander) = find_builtin_macro(&mac.name) {
                 match expander {
@@ -2409,7 +2395,7 @@
                 self.def_collector
                     .def_map
                     .diagnostics
-                    .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, ast_id));
+                    .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id));
                 return;
             }
         } else {
@@ -2420,16 +2406,13 @@
 
         let macro_id = Macro2Loc {
             container: module,
-            id: ItemTreeId::new(self.tree_id, id),
+            id: InFile::new(self.file_id(), ast_id),
             expander,
             allow_internal_unsafe,
             edition: self.def_collector.def_map.data.edition,
         }
         .intern(self.def_collector.db);
-        self.def_collector.def_map.macro_def_to_macro_id.insert(
-            InFile::new(self.file_id(), self.item_tree[id].ast_id()).erase(),
-            macro_id.into(),
-        );
+        self.def_collector.def_map.macro_def_to_macro_id.insert(f_ast_id.erase(), macro_id.into());
         self.def_collector.define_macro_def(
             self.module_id,
             mac.name.clone(),
@@ -2441,16 +2424,17 @@
                 Arc::get_mut(&mut self.def_collector.def_map.data)
                     .unwrap()
                     .exported_derives
-                    .insert(self.def_collector.db.macro_def(macro_id.into()), helpers);
+                    .insert(macro_id.into(), helpers);
             }
         }
     }
 
     fn collect_macro_call(
         &mut self,
-        &MacroCall { ref path, ast_id, expand_to, ctxt }: &MacroCall,
+        ast_id: FileAstId<ast::MacroCall>,
         container: ItemContainerId,
     ) {
+        let &MacroCall { ref path, expand_to, ctxt } = &self.item_tree[ast_id];
         let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path.clone());
         let db = self.def_collector.db;
 
@@ -2565,16 +2549,16 @@
         self.def_collector.cfg_options.check(cfg) != Some(false)
     }
 
-    fn emit_unconfigured_diagnostic(&mut self, tree_id: TreeId, item: AttrOwner, cfg: &CfgExpr) {
+    fn emit_unconfigured_diagnostic(&mut self, ast_id: ErasedAstId, cfg: &CfgExpr) {
         self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
             self.module_id,
-            tree_id,
-            item,
+            ast_id,
             cfg.clone(),
             self.def_collector.cfg_options.clone(),
         ));
     }
 
+    #[inline]
     fn file_id(&self) -> HirFileId {
         self.tree_id.file_id()
     }
diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs
index de3d2f4..c495a07 100644
--- a/crates/hir-def/src/nameres/diagnostics.rs
+++ b/crates/hir-def/src/nameres/diagnostics.rs
@@ -3,22 +3,18 @@
 use std::ops::Not;
 
 use cfg::{CfgExpr, CfgOptions};
-use hir_expand::{ExpandErrorKind, MacroCallKind, attrs::AttrId, mod_path::ModPath};
+use hir_expand::{ErasedAstId, ExpandErrorKind, MacroCallKind, attrs::AttrId, mod_path::ModPath};
 use la_arena::Idx;
 use syntax::ast;
 
-use crate::{
-    AstId,
-    item_tree::{self, AttrOwner, ItemTreeId, TreeId},
-    nameres::LocalModuleId,
-};
+use crate::{AstId, nameres::LocalModuleId};
 
 #[derive(Debug, PartialEq, Eq)]
 pub enum DefDiagnosticKind {
     UnresolvedModule { ast: AstId<ast::Module>, candidates: Box<[String]> },
     UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
-    UnresolvedImport { id: ItemTreeId<item_tree::Use>, index: Idx<ast::UseTree> },
-    UnconfiguredCode { tree: TreeId, item: AttrOwner, cfg: CfgExpr, opts: CfgOptions },
+    UnresolvedImport { id: AstId<ast::Use>, index: Idx<ast::UseTree> },
+    UnconfiguredCode { ast_id: ErasedAstId, cfg: CfgExpr, opts: CfgOptions },
     UnresolvedMacroCall { ast: MacroCallKind, path: ModPath },
     UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
     InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
@@ -28,7 +24,7 @@
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct DefDiagnostics(Option<triomphe::Arc<Box<[DefDiagnostic]>>>);
+pub struct DefDiagnostics(Option<triomphe::ThinArc<(), DefDiagnostic>>);
 
 impl DefDiagnostics {
     pub fn new(diagnostics: Vec<DefDiagnostic>) -> Self {
@@ -36,12 +32,12 @@
             diagnostics
                 .is_empty()
                 .not()
-                .then(|| triomphe::Arc::new(diagnostics.into_boxed_slice())),
+                .then(|| triomphe::ThinArc::from_header_and_iter((), diagnostics.into_iter())),
         )
     }
 
     pub fn iter(&self) -> impl Iterator<Item = &DefDiagnostic> {
-        self.0.as_ref().into_iter().flat_map(|it| &***it)
+        self.0.as_ref().into_iter().flat_map(|it| &it.slice)
     }
 }
 
@@ -75,7 +71,7 @@
 
     pub(super) fn unresolved_import(
         container: LocalModuleId,
-        id: ItemTreeId<item_tree::Use>,
+        id: AstId<ast::Use>,
         index: Idx<ast::UseTree>,
     ) -> Self {
         Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
@@ -92,14 +88,13 @@
 
     pub fn unconfigured_code(
         container: LocalModuleId,
-        tree: TreeId,
-        item: AttrOwner,
+        ast_id: ErasedAstId,
         cfg: CfgExpr,
         opts: CfgOptions,
     ) -> Self {
         Self {
             in_module: container,
-            kind: DefDiagnosticKind::UnconfiguredCode { tree, item, cfg, opts },
+            kind: DefDiagnosticKind::UnconfiguredCode { ast_id, cfg, opts },
         }
     }
 
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 74ce33a..e8235b1 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -12,7 +12,6 @@
 
 use either::Either;
 use hir_expand::{
-    Lookup,
     mod_path::{ModPath, PathKind},
     name::Name,
 };
@@ -107,7 +106,7 @@
         visibility: &RawVisibility,
         within_impl: bool,
     ) -> Option<Visibility> {
-        let mut vis = match visibility {
+        let vis = match visibility {
             RawVisibility::Module(path, explicitness) => {
                 let (result, remaining) = self.resolve_path(
                     local_def_map,
@@ -121,29 +120,36 @@
                     return None;
                 }
                 let types = result.take_types()?;
-                match types {
+                let mut vis = match types {
                     ModuleDefId::ModuleId(m) => Visibility::Module(m, *explicitness),
                     // error: visibility needs to refer to module
                     _ => {
                         return None;
                     }
+                };
+
+                // In block expressions, `self` normally refers to the containing non-block module, and
+                // `super` to its parent (etc.). However, visibilities must only refer to a module in the
+                // DefMap they're written in, so we restrict them when that happens.
+                if let Visibility::Module(m, mv) = vis {
+                    // ...unless we're resolving visibility for an associated item in an impl.
+                    if self.block_id() != m.block && !within_impl {
+                        vis = Visibility::Module(self.module_id(Self::ROOT), mv);
+                        tracing::debug!(
+                            "visibility {:?} points outside DefMap, adjusting to {:?}",
+                            m,
+                            vis
+                        );
+                    }
                 }
+                vis
+            }
+            RawVisibility::PubSelf(explicitness) => {
+                Visibility::Module(self.module_id(original_module), *explicitness)
             }
             RawVisibility::Public => Visibility::Public,
+            RawVisibility::PubCrate => Visibility::PubCrate(self.krate),
         };
-
-        // In block expressions, `self` normally refers to the containing non-block module, and
-        // `super` to its parent (etc.). However, visibilities must only refer to a module in the
-        // DefMap they're written in, so we restrict them when that happens.
-        if let Visibility::Module(m, mv) = vis {
-            // ...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), mv);
-                tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
-            }
-        }
-
         Some(vis)
     }
 
@@ -529,23 +535,22 @@
                     // enum variant
                     cov_mark::hit!(can_import_enum_variant);
 
-                    let res =
-                        db.enum_variants(e).variants.iter().find(|(_, name)| name == segment).map(
-                            |&(variant, _)| {
-                                let item_tree_id = variant.lookup(db).id;
-                                match item_tree_id.item_tree(db)[item_tree_id.value].shape {
-                                    FieldsShape::Record => {
-                                        PerNs::types(variant.into(), Visibility::Public, None)
-                                    }
-                                    FieldsShape::Tuple | FieldsShape::Unit => PerNs::both(
-                                        variant.into(),
-                                        variant.into(),
-                                        Visibility::Public,
-                                        None,
-                                    ),
-                                }
-                            },
-                        );
+                    let res = e
+                        .enum_variants(db)
+                        .variants
+                        .iter()
+                        .find(|(_, name, _)| name == segment)
+                        .map(|&(variant, _, shape)| match shape {
+                            FieldsShape::Record => {
+                                PerNs::types(variant.into(), Visibility::Public, None)
+                            }
+                            FieldsShape::Tuple | FieldsShape::Unit => PerNs::both(
+                                variant.into(),
+                                variant.into(),
+                                Visibility::Public,
+                                None,
+                            ),
+                        });
                     // FIXME: Need to filter visibility here and below? Not sure.
                     return match res {
                         Some(res) => {
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 948e8be..ba75dca 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -2,13 +2,13 @@
     CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
     DependencyBuilder, Env, RootQueryDb, SourceDatabase,
 };
+use expect_test::{Expect, expect};
 use intern::Symbol;
 use span::Edition;
 use test_fixture::WithFixture;
 use triomphe::Arc;
 
 use crate::{
-    AdtId, ModuleDefId,
     db::DefDatabase,
     nameres::{crate_def_map, tests::TestDB},
 };
@@ -16,29 +16,29 @@
 fn check_def_map_is_not_recomputed(
     #[rust_analyzer::rust_fixture] ra_fixture_initial: &str,
     #[rust_analyzer::rust_fixture] ra_fixture_change: &str,
+    expecta: Expect,
+    expectb: Expect,
 ) {
     let (mut db, pos) = TestDB::with_position(ra_fixture_initial);
     let krate = db.fetch_test_crate();
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             crate_def_map(&db, krate);
-        });
-        assert!(
-            format!("{events:?}").contains("crate_local_def_map"),
-            "no crate def map computed:\n{events:#?}",
-        )
-    }
+        },
+        &[],
+        expecta,
+    );
     db.set_file_text(pos.file_id.file_id(&db), ra_fixture_change);
 
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             crate_def_map(&db, krate);
-        });
-        assert!(
-            !format!("{events:?}").contains("crate_local_def_map"),
-            "crate def map invalidated:\n{events:#?}",
-        )
-    }
+        },
+        &[("crate_local_def_map", 0)],
+        expectb,
+    );
 }
 
 #[test]
@@ -104,15 +104,20 @@
         Arc::ptr_eq(&all_crates_before, &all_crates_after),
         "the all_crates list should not have been invalidated"
     );
-
-    let events = db.log_executed(|| {
-        for &krate in db.all_crates().iter() {
-            crate_def_map(&db, krate);
-        }
-    });
-    let invalidated_def_maps =
-        events.iter().filter(|event| event.contains("crate_local_def_map")).count();
-    assert_eq!(invalidated_def_maps, 1, "{events:#?}")
+    execute_assert_events(
+        &db,
+        || {
+            for &krate in db.all_crates().iter() {
+                crate_def_map(&db, krate);
+            }
+        },
+        &[("crate_local_def_map", 1)],
+        expect![[r#"
+            [
+                "crate_local_def_map",
+            ]
+        "#]],
+    );
 }
 
 #[test]
@@ -152,6 +157,33 @@
 #[cfg(never)]
 fn no() {}
 ",
+        expect![[r#"
+            [
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "of_",
+            ]
+        "#]],
+        expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "of_",
+            ]
+        "#]],
     );
 }
 
@@ -183,6 +215,41 @@
 
 pub struct S {}
 ",
+        expect![[r#"
+            [
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "macro_def_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "macro_arg_shim",
+                "decl_macro_expander_shim",
+            ]
+        "#]],
+        expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "macro_arg_shim",
+                "parse_macro_expansion_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+            ]
+        "#]],
     );
 }
 
@@ -206,6 +273,49 @@
 #[proc_macros::identity]
 fn f() { foo }
 ",
+        expect![[r#"
+            [
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "crate_local_def_map",
+                "proc_macros_for_crate_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "macro_def_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "expand_proc_macro_shim",
+                "macro_arg_shim",
+                "proc_macro_span_shim",
+            ]
+        "#]],
+        expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "macro_arg_shim",
+                "expand_proc_macro_shim",
+                "parse_macro_expansion_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+            ]
+        "#]],
     );
 }
 
@@ -287,6 +397,60 @@
 #[derive(proc_macros::DeriveIdentity)]
 pub struct S {}
 ",
+        expect![[r#"
+            [
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "macro_def_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "macro_arg_shim",
+                "decl_macro_expander_shim",
+                "macro_def_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "macro_arg_shim",
+                "decl_macro_expander_shim",
+                "crate_local_def_map",
+                "proc_macros_for_crate_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "macro_def_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "expand_proc_macro_shim",
+                "macro_arg_shim",
+                "proc_macro_span_shim",
+            ]
+        "#]],
+        expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "macro_arg_shim",
+                "macro_arg_shim",
+                "decl_macro_expander_shim",
+                "macro_arg_shim",
+            ]
+        "#]],
     );
 }
 
@@ -341,19 +505,46 @@
 "#,
     );
     let krate = db.test_crate();
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let crate_def_map = crate_def_map(&db, krate);
             let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
             assert_eq!(module_data.scope.resolutions().count(), 4);
-        });
-        let n_recalculated_item_trees =
-            events.iter().filter(|it| it.contains("file_item_tree_shim")).count();
-        assert_eq!(n_recalculated_item_trees, 6);
-        let n_reparsed_macros =
-            events.iter().filter(|it| it.contains("parse_macro_expansion_shim")).count();
-        assert_eq!(n_reparsed_macros, 3);
-    }
+        },
+        &[("file_item_tree_query", 6), ("parse_macro_expansion_shim", 3)],
+        expect![[r#"
+            [
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "macro_def_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "macro_arg_shim",
+                "decl_macro_expander_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "macro_arg_shim",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_macro_expansion_shim",
+                "macro_arg_shim",
+            ]
+        "#]],
+    );
 
     let new_text = r#"
 m!(X);
@@ -363,28 +554,31 @@
 "#;
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let crate_def_map = crate_def_map(&db, krate);
             let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
             assert_eq!(module_data.scope.resolutions().count(), 4);
-        });
-        let n_recalculated_item_trees =
-            events.iter().filter(|it| it.contains("file_item_tree_shim")).count();
-        assert_eq!(n_recalculated_item_trees, 1, "{events:#?}");
-        let n_reparsed_macros =
-            events.iter().filter(|it| it.contains("parse_macro_expansion_shim")).count();
-        assert_eq!(n_reparsed_macros, 0);
-    }
+        },
+        &[("file_item_tree_query", 1), ("parse_macro_expansion_shim", 0)],
+        expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "macro_arg_shim",
+                "macro_arg_shim",
+                "macro_arg_shim",
+            ]
+        "#]],
+    );
 }
 
 #[test]
 fn item_tree_prevents_reparsing() {
-    // The `ItemTree` is used by both name resolution and the various queries in `adt.rs` and
-    // `data.rs`. After computing the `ItemTree` and deleting the parse tree, we should be able to
-    // run those other queries without triggering a reparse.
-
-    let (db, pos) = TestDB::with_position(
+    let (mut db, pos) = TestDB::with_position(
         r#"
 pub struct S;
 pub union U {}
@@ -399,53 +593,54 @@
 pub type Ty = ();
 "#,
     );
-    let krate = db.test_crate();
-    {
-        let events = db.log_executed(|| {
+
+    execute_assert_events(
+        &db,
+        || {
             db.file_item_tree(pos.file_id.into());
-        });
-        let n_calculated_item_trees =
-            events.iter().filter(|it| it.contains("file_item_tree_shim")).count();
-        assert_eq!(n_calculated_item_trees, 1);
-        let n_parsed_files = events.iter().filter(|it| it.contains("parse")).count();
-        assert_eq!(n_parsed_files, 1);
+        },
+        &[("file_item_tree_query", 1), ("parse", 1)],
+        expect![[r#"
+            [
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+            ]
+        "#]],
+    );
+
+    let file_id = pos.file_id.file_id(&db);
+    let file_text = db.file_text(file_id).text(&db);
+    db.set_file_text(file_id, &format!("{file_text}\n"));
+
+    execute_assert_events(
+        &db,
+        || {
+            db.file_item_tree(pos.file_id.into());
+        },
+        &[("file_item_tree_query", 1), ("parse", 1)],
+        expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+            ]
+        "#]],
+    );
+}
+
+fn execute_assert_events(
+    db: &TestDB,
+    f: impl FnOnce(),
+    required: &[(&str, usize)],
+    expect: Expect,
+) {
+    let events = db.log_executed(f);
+    for (event, count) in required {
+        let n = events.iter().filter(|it| it.contains(event)).count();
+        assert_eq!(n, *count, "Expected {event} to be executed {count} times, but only got {n}");
     }
-
-    // FIXME(salsa-transition): bring this back
-    // base_db::ParseQuery.in_db(&db).purge();
-
-    {
-        let events = db.log_executed(|| {
-            let crate_def_map = crate_def_map(&db, krate);
-            let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
-            assert_eq!(module_data.scope.resolutions().count(), 8);
-            assert_eq!(module_data.scope.impls().count(), 1);
-
-            for imp in module_data.scope.impls() {
-                db.impl_signature(imp);
-            }
-
-            for (_, res) in module_data.scope.resolutions() {
-                match res.values.map(|it| it.def).or(res.types.map(|it| it.def)).unwrap() {
-                    ModuleDefId::FunctionId(f) => _ = db.function_signature(f),
-                    ModuleDefId::AdtId(adt) => match adt {
-                        AdtId::StructId(it) => _ = db.struct_signature(it),
-                        AdtId::UnionId(it) => _ = db.union_signature(it),
-                        AdtId::EnumId(it) => _ = db.enum_signature(it),
-                    },
-                    ModuleDefId::ConstId(it) => _ = db.const_signature(it),
-                    ModuleDefId::StaticId(it) => _ = db.static_signature(it),
-                    ModuleDefId::TraitId(it) => _ = db.trait_signature(it),
-                    ModuleDefId::TraitAliasId(it) => _ = db.trait_alias_signature(it),
-                    ModuleDefId::TypeAliasId(it) => _ = db.type_alias_signature(it),
-                    ModuleDefId::EnumVariantId(_)
-                    | ModuleDefId::ModuleId(_)
-                    | ModuleDefId::MacroId(_)
-                    | ModuleDefId::BuiltinType(_) => unreachable!(),
-                }
-            }
-        });
-        let n_reparsed_files = events.iter().filter(|it| it.contains("parse(")).count();
-        assert_eq!(n_reparsed_files, 0);
-    }
+    expect.assert_debug_eq(&events);
 }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 16988dd..6f32198 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -5,21 +5,22 @@
 use hir_expand::{
     MacroDefId,
     mod_path::{ModPath, PathKind},
-    name::Name,
+    name::{AsName, Name},
 };
 use intern::{Symbol, sym};
 use itertools::Itertools as _;
 use rustc_hash::FxHashSet;
 use smallvec::{SmallVec, smallvec};
 use span::SyntaxContext;
+use syntax::ast::HasName;
 use triomphe::Arc;
 
 use crate::{
-    AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
-    ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule,
-    ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id,
-    MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId,
-    TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId,
+    AdtId, AstIdLoc, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
+    EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId,
+    GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup,
+    Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId,
+    TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId,
     builtin_type::BuiltinType,
     db::DefDatabase,
     expr_store::{
@@ -32,10 +33,10 @@
         generics::{GenericParams, TypeOrConstParamData},
     },
     item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope},
-    item_tree::ImportAlias,
     lang_item::LangItemTarget,
     nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map},
     per_ns::PerNs,
+    src::HasSource,
     type_ref::LifetimeRef,
     visibility::{RawVisibility, Visibility},
 };
@@ -304,6 +305,10 @@
                     }),
                 )
             }
+            RawVisibility::PubSelf(explicitness) => {
+                Some(Visibility::Module(self.module(), *explicitness))
+            }
+            RawVisibility::PubCrate => Some(Visibility::PubCrate(self.krate())),
             RawVisibility::Public => Some(Visibility::Public),
         }
     }
@@ -627,14 +632,14 @@
             .extern_crate_decls()
             .filter_map(|id| {
                 let loc = id.lookup(db);
-                let tree = loc.item_tree_id().item_tree(db);
-                match &tree[loc.id.value].alias {
-                    Some(alias) => match alias {
-                        ImportAlias::Underscore => None,
-                        ImportAlias::Alias(name) => Some(name.clone()),
-                    },
-                    None => Some(tree[loc.id.value].name.clone()),
-                }
+                let extern_crate = loc.source(db);
+                // If there is a rename (`as x`), extract the renamed name, or remove the `extern crate`
+                // if it is an underscore.
+                extern_crate
+                    .value
+                    .rename()
+                    .map(|a| a.name().map(|it| it.as_name()))
+                    .unwrap_or_else(|| extern_crate.value.name_ref().map(|it| it.as_name()))
             })
     }
 
@@ -1471,10 +1476,7 @@
 
 fn lookup_resolver(
     db: &dyn DefDatabase,
-    lookup: impl Lookup<
-        Database = dyn DefDatabase,
-        Data = impl ItemTreeLoc<Container = impl HasResolver>,
-    >,
+    lookup: impl Lookup<Database = dyn DefDatabase, Data = impl AstIdLoc<Container = impl HasResolver>>,
 ) -> Resolver<'_> {
     lookup.lookup(db).container().resolver(db)
 }
diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs
index 44cfd72..377a545 100644
--- a/crates/hir-def/src/signatures.rs
+++ b/crates/hir-def/src/signatures.rs
@@ -4,21 +4,25 @@
 
 use bitflags::bitflags;
 use cfg::{CfgExpr, CfgOptions};
-use either::Either;
-use hir_expand::{InFile, Intern, Lookup, name::Name};
+use hir_expand::{
+    InFile, Intern, Lookup,
+    name::{AsName, Name},
+};
 use intern::{Symbol, sym};
 use la_arena::{Arena, Idx};
 use rustc_abi::{IntegerType, ReprOptions};
 use syntax::{
-    AstNode, SyntaxNodePtr,
-    ast::{self, HasGenericParams, IsString},
+    NodeOrToken, SyntaxNodePtr, T,
+    ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
 };
 use thin_vec::ThinVec;
 use triomphe::Arc;
 
 use crate::{
-    ConstId, EnumId, EnumVariantId, EnumVariantLoc, FunctionId, HasModule, ImplId, ItemContainerId,
-    ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId, VariantId,
+    ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId,
+    ItemContainerId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId,
+    VariantId,
+    attr::Attrs,
     db::DefDatabase,
     expr_store::{
         ExpressionStore, ExpressionStoreSourceMap,
@@ -28,15 +32,17 @@
         },
     },
     hir::{ExprId, PatId, generics::GenericParams},
-    item_tree::{
-        AttrOwner, Field, FieldParent, FieldsShape, FileItemTreeId, ItemTree, ItemTreeId, ModItem,
-        RawVisibility, RawVisibilityId,
-    },
+    item_tree::{FieldsShape, RawVisibility, visibility_from_ast},
     lang_item::LangItem,
     src::HasSource,
     type_ref::{TraitRef, TypeBound, TypeRefId},
 };
 
+#[inline]
+fn as_name_opt(name: Option<ast::Name>) -> Name {
+    name.map_or_else(Name::missing, |it| it.as_name())
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub struct StructSignature {
     pub name: Name,
@@ -70,8 +76,8 @@
 impl StructSignature {
     pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+        let InFile { file_id, value: source } = loc.source(db);
+        let attrs = db.attrs(id.into());
 
         let mut flags = StructFlags::empty();
         if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
@@ -91,23 +97,23 @@
             }
         }
         let repr = attrs.repr();
+        let shape = adt_shape(source.kind());
 
-        let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db);
         let (store, generic_params, source_map) = lower_generic_params(
             db,
             loc.container,
             id.into(),
             file_id,
-            value.generic_param_list(),
-            value.where_clause(),
+            source.generic_param_list(),
+            source.where_clause(),
         );
         (
             Arc::new(StructSignature {
                 generic_params,
                 store,
                 flags,
-                shape: item_tree[loc.id.value].shape,
-                name: item_tree[loc.id.value].name.clone(),
+                shape,
+                name: as_name_opt(source.name()),
                 repr,
             }),
             Arc::new(source_map),
@@ -115,6 +121,15 @@
     }
 }
 
+#[inline]
+fn adt_shape(adt_kind: ast::StructKind) -> FieldsShape {
+    match adt_kind {
+        ast::StructKind::Record(_) => FieldsShape::Record,
+        ast::StructKind::Tuple(_) => FieldsShape::Tuple,
+        ast::StructKind::Unit => FieldsShape::Unit,
+    }
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub struct UnionSignature {
     pub name: Name,
@@ -127,9 +142,7 @@
 impl UnionSignature {
     pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let krate = loc.container.krate;
-        let item_tree = loc.id.item_tree(db);
-        let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
+        let attrs = db.attrs(id.into());
         let mut flags = StructFlags::empty();
         if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
@@ -140,14 +153,14 @@
 
         let repr = attrs.repr();
 
-        let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db);
+        let InFile { file_id, value: source } = loc.source(db);
         let (store, generic_params, source_map) = lower_generic_params(
             db,
             loc.container,
             id.into(),
             file_id,
-            value.generic_param_list(),
-            value.where_clause(),
+            source.generic_param_list(),
+            source.where_clause(),
         );
         (
             Arc::new(UnionSignature {
@@ -155,7 +168,7 @@
                 store,
                 flags,
                 repr,
-                name: item_tree[loc.id.value].name.clone(),
+                name: as_name_opt(source.name()),
             }),
             Arc::new(source_map),
         )
@@ -181,8 +194,7 @@
 impl EnumSignature {
     pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+        let attrs = db.attrs(id.into());
         let mut flags = EnumFlags::empty();
         if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
@@ -190,14 +202,14 @@
 
         let repr = attrs.repr();
 
-        let hir_expand::files::InFileWrapper { file_id, value } = loc.source(db);
+        let InFile { file_id, value: source } = loc.source(db);
         let (store, generic_params, source_map) = lower_generic_params(
             db,
             loc.container,
             id.into(),
             file_id,
-            value.generic_param_list(),
-            value.where_clause(),
+            source.generic_param_list(),
+            source.where_clause(),
         );
 
         (
@@ -206,7 +218,7 @@
                 store,
                 flags,
                 repr,
-                name: item_tree[loc.id.value].name.clone(),
+                name: as_name_opt(source.name()),
             }),
             Arc::new(source_map),
         )
@@ -239,10 +251,9 @@
 impl ConstSignature {
     pub fn query(db: &dyn DefDatabase, id: ConstId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
 
         let module = loc.container.module(db);
-        let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into());
+        let attrs = db.attrs(id.into());
         let mut flags = ConstFlags::empty();
         if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
@@ -253,14 +264,14 @@
         }
 
         let (store, source_map, type_ref) =
-            crate::expr_store::lower::lower_type_ref(db, module, source.map(|it| it.ty()));
+            crate::expr_store::lower::lower_type_ref(db, module, source.as_ref().map(|it| it.ty()));
 
         (
             Arc::new(ConstSignature {
                 store: Arc::new(store),
                 type_ref,
                 flags,
-                name: item_tree[loc.id.value].name.clone(),
+                name: source.value.name().map(|it| it.as_name()),
             }),
             Arc::new(source_map),
         )
@@ -295,10 +306,9 @@
 impl StaticSignature {
     pub fn query(db: &dyn DefDatabase, id: StaticId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
 
         let module = loc.container.module(db);
-        let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into());
+        let attrs = db.attrs(id.into());
         let mut flags = StaticFlags::empty();
         if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
@@ -323,14 +333,14 @@
         }
 
         let (store, source_map, type_ref) =
-            crate::expr_store::lower::lower_type_ref(db, module, source.map(|it| it.ty()));
+            crate::expr_store::lower::lower_type_ref(db, module, source.as_ref().map(|it| it.ty()));
 
         (
             Arc::new(StaticSignature {
                 store: Arc::new(store),
                 type_ref,
                 flags,
-                name: item_tree[loc.id.value].name.clone(),
+                name: as_name_opt(source.value.name()),
             }),
             Arc::new(source_map),
         )
@@ -407,10 +417,9 @@
 impl TraitSignature {
     pub fn query(db: &dyn DefDatabase, id: TraitId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
 
         let mut flags = TraitFlags::empty();
-        let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+        let attrs = db.attrs(id.into());
         let source = loc.source(db);
         if source.value.auto_token().is_some() {
             flags.insert(TraitFlags::AUTO);
@@ -446,15 +455,11 @@
             flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
         }
 
+        let name = as_name_opt(source.value.name());
         let (store, source_map, generic_params) = lower_trait(db, loc.container, source, id);
 
         (
-            Arc::new(TraitSignature {
-                store: Arc::new(store),
-                generic_params,
-                flags,
-                name: item_tree[loc.id.value].name.clone(),
-            }),
+            Arc::new(TraitSignature { store: Arc::new(store), generic_params, flags, name }),
             Arc::new(source_map),
         )
     }
@@ -473,17 +478,13 @@
         id: TraitAliasId,
     ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
 
         let source = loc.source(db);
+        let name = as_name_opt(source.value.name());
         let (store, source_map, generic_params) = lower_trait_alias(db, loc.container, source, id);
 
         (
-            Arc::new(TraitAliasSignature {
-                generic_params,
-                store: Arc::new(store),
-                name: item_tree[loc.id.value].name.clone(),
-            }),
+            Arc::new(TraitAliasSignature { generic_params, store: Arc::new(store), name }),
             Arc::new(source_map),
         )
     }
@@ -530,10 +531,9 @@
     ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
         let module = loc.container.module(db);
-        let item_tree = loc.id.item_tree(db);
 
         let mut flags = FnFlags::empty();
-        let attrs = item_tree.attrs(db, module.krate, ModItem::from(loc.id.value).into());
+        let attrs = db.attrs(id.into());
         if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
             flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
         }
@@ -568,6 +568,7 @@
             flags.insert(FnFlags::HAS_BODY);
         }
 
+        let name = as_name_opt(source.value.name());
         let abi = source.value.abi().map(|abi| {
             abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes()))
         });
@@ -588,7 +589,7 @@
                 abi,
                 flags,
                 legacy_const_generics_indices,
-                name: item_tree[loc.id.value].name.clone(),
+                name,
             }),
             Arc::new(source_map),
         )
@@ -662,14 +663,9 @@
         id: TypeAliasId,
     ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
         let loc = id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
 
         let mut flags = TypeAliasFlags::empty();
-        let attrs = item_tree.attrs(
-            db,
-            loc.container.module(db).krate(),
-            ModItem::from(loc.id.value).into(),
-        );
+        let attrs = db.attrs(id.into());
         if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
             flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL);
         }
@@ -680,6 +676,7 @@
             flags.insert(TypeAliasFlags::IS_EXTERN);
         }
         let source = loc.source(db);
+        let name = as_name_opt(source.value.name());
         let (store, source_map, generic_params, bounds, ty) =
             lower_type_alias(db, loc.container.module(db), source, id);
 
@@ -689,7 +686,7 @@
                 generic_params,
                 flags,
                 bounds,
-                name: item_tree[loc.id.value].name.clone(),
+                name,
                 ty,
             }),
             Arc::new(source_map),
@@ -743,104 +740,41 @@
         let (shape, (fields, store, source_map)) = match id {
             VariantId::EnumVariantId(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
                 let parent = loc.parent.lookup(db);
-                let variant = &item_tree[loc.id.value];
-                (
-                    variant.shape,
-                    lower_fields(
-                        db,
-                        parent.container,
-                        &item_tree,
-                        FieldParent::EnumVariant(loc.id.value),
-                        loc.source(db).map(|src| {
-                            variant.fields.iter().zip(
-                                src.field_list()
-                                    .map(|it| {
-                                        match it {
-                                            ast::FieldList::RecordFieldList(record_field_list) => {
-                                                Either::Left(record_field_list.fields().map(|it| {
-                                                    (SyntaxNodePtr::new(it.syntax()), it.ty())
-                                                }))
-                                            }
-                                            ast::FieldList::TupleFieldList(field_list) => {
-                                                Either::Right(field_list.fields().map(|it| {
-                                                    (SyntaxNodePtr::new(it.syntax()), it.ty())
-                                                }))
-                                            }
-                                        }
-                                        .into_iter()
-                                    })
-                                    .into_iter()
-                                    .flatten(),
-                            )
-                        }),
-                        Some(item_tree[parent.id.value].visibility),
-                    ),
-                )
+                let source = loc.source(db);
+                let shape = adt_shape(source.value.kind());
+                let span_map = db.span_map(source.file_id);
+                let override_visibility = visibility_from_ast(
+                    db,
+                    source.value.parent_enum().visibility(),
+                    &mut |range| span_map.span_for_range(range).ctx,
+                );
+                let fields = lower_field_list(
+                    db,
+                    parent.container,
+                    source.map(|src| src.field_list()),
+                    Some(override_visibility),
+                );
+                (shape, fields)
             }
             VariantId::StructId(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
-                let strukt = &item_tree[loc.id.value];
-                (
-                    strukt.shape,
-                    lower_fields(
-                        db,
-                        loc.container,
-                        &item_tree,
-                        FieldParent::Struct(loc.id.value),
-                        loc.source(db).map(|src| {
-                            strukt.fields.iter().zip(
-                                src.field_list()
-                                    .map(|it| {
-                                        match it {
-                                            ast::FieldList::RecordFieldList(record_field_list) => {
-                                                Either::Left(record_field_list.fields().map(|it| {
-                                                    (SyntaxNodePtr::new(it.syntax()), it.ty())
-                                                }))
-                                            }
-                                            ast::FieldList::TupleFieldList(field_list) => {
-                                                Either::Right(field_list.fields().map(|it| {
-                                                    (SyntaxNodePtr::new(it.syntax()), it.ty())
-                                                }))
-                                            }
-                                        }
-                                        .into_iter()
-                                    })
-                                    .into_iter()
-                                    .flatten(),
-                            )
-                        }),
-                        None,
-                    ),
-                )
+                let source = loc.source(db);
+                let shape = adt_shape(source.value.kind());
+                let fields =
+                    lower_field_list(db, loc.container, source.map(|src| src.field_list()), None);
+                (shape, fields)
             }
             VariantId::UnionId(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
-                let union = &item_tree[loc.id.value];
-                (
-                    FieldsShape::Record,
-                    lower_fields(
-                        db,
-                        loc.container,
-                        &item_tree,
-                        FieldParent::Union(loc.id.value),
-                        loc.source(db).map(|src| {
-                            union.fields.iter().zip(
-                                src.record_field_list()
-                                    .map(|it| {
-                                        it.fields()
-                                            .map(|it| (SyntaxNodePtr::new(it.syntax()), it.ty()))
-                                    })
-                                    .into_iter()
-                                    .flatten(),
-                            )
-                        }),
-                        None,
-                    ),
-                )
+                let source = loc.source(db);
+                let fields = lower_field_list(
+                    db,
+                    loc.container,
+                    source.map(|src| src.record_field_list().map(ast::FieldList::RecordFieldList)),
+                    None,
+                );
+                (FieldsShape::Record, fields)
             }
         };
 
@@ -860,39 +794,81 @@
     }
 }
 
-fn lower_fields<'a>(
+fn lower_field_list(
     db: &dyn DefDatabase,
     module: ModuleId,
-    item_tree: &ItemTree,
-    parent: FieldParent,
-    fields: InFile<impl Iterator<Item = (&'a Field, (SyntaxNodePtr, Option<ast::Type>))>>,
-    override_visibility: Option<RawVisibilityId>,
+    fields: InFile<Option<ast::FieldList>>,
+    override_visibility: Option<RawVisibility>,
+) -> (Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap) {
+    let file_id = fields.file_id;
+    match fields.value {
+        Some(ast::FieldList::RecordFieldList(fields)) => lower_fields(
+            db,
+            module,
+            InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
+            |_, field| as_name_opt(field.name()),
+            override_visibility,
+        ),
+        Some(ast::FieldList::TupleFieldList(fields)) => lower_fields(
+            db,
+            module,
+            InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
+            |idx, _| Name::new_tuple_field(idx),
+            override_visibility,
+        ),
+        None => lower_fields(
+            db,
+            module,
+            InFile::new(file_id, std::iter::empty::<(Option<ast::Type>, ast::RecordField)>()),
+            |_, _| Name::missing(),
+            None,
+        ),
+    }
+}
+
+fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
+    db: &dyn DefDatabase,
+    module: ModuleId,
+    fields: InFile<impl Iterator<Item = (Option<ast::Type>, Field)>>,
+    mut field_name: impl FnMut(usize, &Field) -> Name,
+    override_visibility: Option<RawVisibility>,
 ) -> (Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap) {
     let mut arena = Arena::new();
     let cfg_options = module.krate.cfg_options(db);
     let mut col = ExprCollector::new(db, module, fields.file_id);
-    for (idx, (field, (ptr, ty))) in fields.value.enumerate() {
-        let attr_owner = AttrOwner::make_field_indexed(parent, idx);
-        let attrs = item_tree.attrs(db, module.krate, attr_owner);
-        if attrs.is_cfg_enabled(cfg_options) {
-            arena.alloc(FieldData {
-                name: field.name.clone(),
-                type_ref: col
-                    .lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator),
-                visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
-                is_unsafe: field.is_unsafe,
-            });
-        } else {
-            col.source_map.diagnostics.push(
-                crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
-                    node: InFile::new(fields.file_id, ptr),
-                    cfg: attrs.cfg().unwrap(),
-                    opts: cfg_options.clone(),
-                },
-            );
+    let mut idx = 0;
+    for (ty, field) in fields.value {
+        match Attrs::is_cfg_enabled_for(db, &field, col.span_map(), cfg_options) {
+            Ok(()) => {
+                let type_ref =
+                    col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator);
+                let visibility = override_visibility.clone().unwrap_or_else(|| {
+                    visibility_from_ast(db, field.visibility(), &mut |range| {
+                        col.span_map().span_for_range(range).ctx
+                    })
+                });
+                let is_unsafe = field
+                    .syntax()
+                    .children_with_tokens()
+                    .filter_map(NodeOrToken::into_token)
+                    .any(|token| token.kind() == T![unsafe]);
+                let name = field_name(idx, &field);
+                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });
+                idx += 1;
+            }
+            Err(cfg) => {
+                col.source_map.diagnostics.push(
+                    crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
+                        node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
+                        cfg,
+                        opts: cfg_options.clone(),
+                    },
+                );
+            }
         }
     }
     let store = col.store.finish();
+    arena.shrink_to_fit();
     (arena, store, col.source_map)
 }
 
@@ -905,56 +881,71 @@
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct EnumVariants {
-    pub variants: Box<[(EnumVariantId, Name)]>,
+    pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>,
 }
 
+#[salsa::tracked]
 impl EnumVariants {
-    pub(crate) fn enum_variants_query(
+    #[salsa::tracked(returns(ref))]
+    pub(crate) fn of(
         db: &dyn DefDatabase,
         e: EnumId,
-    ) -> (Arc<EnumVariants>, Option<Arc<ThinVec<InactiveEnumVariantCode>>>) {
+    ) -> (EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
         let loc = e.lookup(db);
-        let item_tree = loc.id.item_tree(db);
+        let source = loc.source(db);
+        let ast_id_map = db.ast_id_map(source.file_id);
+        let span_map = db.span_map(source.file_id);
 
         let mut diagnostics = ThinVec::new();
         let cfg_options = loc.container.krate.cfg_options(db);
         let mut index = 0;
-        let variants = FileItemTreeId::range_iter(item_tree[loc.id.value].variants.clone())
+        let Some(variants) = source.value.variant_list() else {
+            return (EnumVariants { variants: Box::default() }, None);
+        };
+        let variants = variants
+            .variants()
             .filter_map(|variant| {
-                let attrs = item_tree.attrs(db, loc.container.krate, variant.into());
-                if attrs.is_cfg_enabled(cfg_options) {
-                    let enum_variant = EnumVariantLoc {
-                        id: ItemTreeId::new(loc.id.tree_id(), variant),
-                        parent: e,
-                        index,
+                let ast_id = ast_id_map.ast_id(&variant);
+                match Attrs::is_cfg_enabled_for(db, &variant, span_map.as_ref(), cfg_options) {
+                    Ok(()) => {
+                        let enum_variant =
+                            EnumVariantLoc { id: source.with_value(ast_id), parent: e, index }
+                                .intern(db);
+                        index += 1;
+                        let name = as_name_opt(variant.name());
+                        let shape = adt_shape(variant.kind());
+                        Some((enum_variant, name, shape))
                     }
-                    .intern(db);
-                    index += 1;
-                    Some((enum_variant, item_tree[variant].name.clone()))
-                } else {
-                    diagnostics.push(InactiveEnumVariantCode {
-                        ast_id: item_tree[variant].ast_id,
-                        cfg: attrs.cfg().unwrap(),
-                        opts: cfg_options.clone(),
-                    });
-                    None
+                    Err(cfg) => {
+                        diagnostics.push(InactiveEnumVariantCode {
+                            ast_id,
+                            cfg,
+                            opts: cfg_options.clone(),
+                        });
+                        None
+                    }
                 }
             })
             .collect();
 
-        (
-            Arc::new(EnumVariants { variants }),
-            diagnostics.is_empty().not().then(|| Arc::new(diagnostics)),
-        )
+        (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics))
+    }
+}
+
+impl EnumVariants {
+    pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
+        self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None })
     }
 
-    pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
-        self.variants.iter().find_map(|(v, n)| if n == name { Some(*v) } else { None })
+    pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option<Name> {
+        self.variants
+            .iter()
+            .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None })
     }
 
     // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
     pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
-        self.variants.iter().all(|&(v, _)| {
+        self.variants.iter().all(|&(v, _, _)| {
             // The condition check order is slightly modified from rustc
             // to improve performance by early returning with relatively fast checks
             let variant = &db.variant_fields(v.into());
@@ -973,3 +964,17 @@
         })
     }
 }
+
+pub(crate) fn extern_block_abi(
+    db: &dyn DefDatabase,
+    extern_block: ExternBlockId,
+) -> Option<Symbol> {
+    let source = extern_block.lookup(db).source(db);
+    source.value.abi().map(|abi| {
+        match abi.abi_string() {
+            Some(tok) => Symbol::intern(tok.text_without_quotes()),
+            // `extern` default to be `extern "C"`.
+            _ => sym::C,
+        }
+    })
+}
diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs
index 3867f39..aa373a2 100644
--- a/crates/hir-def/src/src.rs
+++ b/crates/hir-def/src/src.rs
@@ -1,15 +1,13 @@
 //! Utilities for mapping between hir IDs and the surface syntax.
 
 use either::Either;
-use hir_expand::InFile;
-use la_arena::ArenaMap;
+use hir_expand::{AstId, InFile};
+use la_arena::{Arena, ArenaMap, Idx};
 use syntax::{AstNode, AstPtr, ast};
 
 use crate::{
-    GenericDefId, ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
-    UseId, VariantId,
-    db::DefDatabase,
-    item_tree::{AttrOwner, FieldParent, ItemTreeNode},
+    AstIdLoc, GenericDefId, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
+    UseId, VariantId, attr::Attrs, db::DefDatabase,
 };
 
 pub trait HasSource {
@@ -23,18 +21,13 @@
 
 impl<T> HasSource for T
 where
-    T: ItemTreeLoc,
-    T::Id: ItemTreeNode,
+    T: AstIdLoc,
 {
-    type Value = <T::Id as ItemTreeNode>::Source;
+    type Value = T::Ast;
     fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile<AstPtr<Self::Value>> {
-        let id = self.item_tree_id();
-        let file_id = id.file_id();
-        let tree = id.item_tree(db);
-        let ast_id_map = db.ast_id_map(file_id);
-        let node = &tree[id.value];
-
-        InFile::new(file_id, ast_id_map.get(node.ast_id()))
+        let id = self.ast_id();
+        let ast_id_map = db.ast_id_map(id.file_id);
+        InFile::new(id.file_id, ast_id_map.get(id.value))
     }
 }
 
@@ -43,18 +36,37 @@
     fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
 }
 
+/// Maps a `UseTree` contained in this import back to its AST node.
+pub fn use_tree_to_ast(
+    db: &dyn DefDatabase,
+    use_ast_id: AstId<ast::Use>,
+    index: Idx<ast::UseTree>,
+) -> ast::UseTree {
+    use_tree_source_map(db, use_ast_id)[index].clone()
+}
+
+/// Maps a `UseTree` contained in this import back to its AST node.
+fn use_tree_source_map(db: &dyn DefDatabase, use_ast_id: AstId<ast::Use>) -> Arena<ast::UseTree> {
+    // Re-lower the AST item and get the source map.
+    // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
+    let ast = use_ast_id.to_node(db);
+    let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
+    let mut span_map = None;
+    crate::item_tree::lower_use_tree(db, ast_use_tree, &mut |range| {
+        span_map.get_or_insert_with(|| db.span_map(use_ast_id.file_id)).span_for_range(range).ctx
+    })
+    .expect("failed to lower use tree")
+    .1
+}
+
 impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
     type Value = ast::UseTree;
     fn child_source(
         &self,
         db: &dyn DefDatabase,
     ) -> InFile<ArenaMap<la_arena::Idx<ast::UseTree>, Self::Value>> {
-        let loc = &self.lookup(db);
-        let use_ = &loc.id.item_tree(db)[loc.id.value];
-        InFile::new(
-            loc.id.file_id(),
-            use_.use_tree_source_map(db, loc.id.file_id()).into_iter().collect(),
-        )
+        let loc = self.lookup(db);
+        InFile::new(loc.id.file_id, use_tree_source_map(db, loc.id).into_iter().collect())
     }
 }
 
@@ -124,49 +136,30 @@
     type Value = Either<ast::TupleField, ast::RecordField>;
 
     fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
-        let item_tree;
-        let (src, parent, container) = match *self {
+        let (src, container) = match *self {
             VariantId::EnumVariantId(it) => {
                 let lookup = it.lookup(db);
-                item_tree = lookup.id.item_tree(db);
-                (
-                    lookup.source(db).map(|it| it.kind()),
-                    FieldParent::EnumVariant(lookup.id.value),
-                    lookup.parent.lookup(db).container,
-                )
+                (lookup.source(db).map(|it| it.kind()), lookup.parent.lookup(db).container)
             }
             VariantId::StructId(it) => {
                 let lookup = it.lookup(db);
-                item_tree = lookup.id.item_tree(db);
-                (
-                    lookup.source(db).map(|it| it.kind()),
-                    FieldParent::Struct(lookup.id.value),
-                    lookup.container,
-                )
+                (lookup.source(db).map(|it| it.kind()), lookup.container)
             }
             VariantId::UnionId(it) => {
                 let lookup = it.lookup(db);
-                item_tree = lookup.id.item_tree(db);
-                (
-                    lookup.source(db).map(|it| it.kind()),
-                    FieldParent::Union(lookup.id.value),
-                    lookup.container,
-                )
+                (lookup.source(db).map(|it| it.kind()), lookup.container)
             }
         };
-
+        let span_map = db.span_map(src.file_id);
         let mut map = ArenaMap::new();
         match &src.value {
             ast::StructKind::Tuple(fl) => {
                 let cfg_options = container.krate.cfg_options(db);
                 let mut idx = 0;
-                for (i, fd) in fl.fields().enumerate() {
-                    let attrs = item_tree.attrs(
-                        db,
-                        container.krate,
-                        AttrOwner::make_field_indexed(parent, i),
-                    );
-                    if !attrs.is_cfg_enabled(cfg_options) {
+                for fd in fl.fields() {
+                    let enabled =
+                        Attrs::is_cfg_enabled_for(db, &fd, span_map.as_ref(), cfg_options).is_ok();
+                    if !enabled {
                         continue;
                     }
                     map.insert(
@@ -179,13 +172,10 @@
             ast::StructKind::Record(fl) => {
                 let cfg_options = container.krate.cfg_options(db);
                 let mut idx = 0;
-                for (i, fd) in fl.fields().enumerate() {
-                    let attrs = item_tree.attrs(
-                        db,
-                        container.krate,
-                        AttrOwner::make_field_indexed(parent, i),
-                    );
-                    if !attrs.is_cfg_enabled(cfg_options) {
+                for fd in fl.fields() {
+                    let enabled =
+                        Attrs::is_cfg_enabled_for(db, &fd, span_map.as_ref(), cfg_options).is_ok();
+                    if !enabled {
                         continue;
                     }
                     map.insert(
@@ -195,7 +185,7 @@
                     idx += 1;
                 }
             }
-            _ => (),
+            ast::StructKind::Unit => (),
         }
         InFile::new(src.file_id, map)
     }
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 3c67ee9..2514e88 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -2,16 +2,15 @@
 
 use std::iter;
 
-use hir_expand::Lookup;
+use base_db::Crate;
+use hir_expand::{InFile, Lookup};
 use la_arena::ArenaMap;
+use syntax::ast::{self, HasVisibility};
 use triomphe::Arc;
 
 use crate::{
-    ConstId, FunctionId, HasModule, ItemContainerId, ItemLoc, ItemTreeLoc, LocalFieldId,
-    LocalModuleId, ModuleId, TraitId, TypeAliasId, VariantId,
-    db::DefDatabase,
-    nameres::DefMap,
-    resolver::{HasResolver, Resolver},
+    AssocItemId, HasModule, ItemContainerId, LocalFieldId, LocalModuleId, ModuleId, TraitId,
+    VariantId, db::DefDatabase, nameres::DefMap, resolver::HasResolver, src::HasSource,
 };
 
 pub use crate::item_tree::{RawVisibility, VisibilityExplicitness};
@@ -21,6 +20,8 @@
 pub enum Visibility {
     /// Visibility is restricted to a certain module.
     Module(ModuleId, VisibilityExplicitness),
+    /// Visibility is restricted to the crate.
+    PubCrate(Crate),
     /// Visibility is unrestricted.
     Public,
 }
@@ -43,8 +44,13 @@
     pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
         let to_module = match self {
             Visibility::Module(m, _) => m,
+            Visibility::PubCrate(krate) => return from_module.krate == krate,
             Visibility::Public => return true,
         };
+        if from_module == to_module {
+            // if the modules are the same, visibility is trivially satisfied
+            return true;
+        }
         // if they're not in the same crate, it can't be visible
         if from_module.krate != to_module.krate {
             return false;
@@ -61,12 +67,18 @@
     ) -> bool {
         let to_module = match self {
             Visibility::Module(m, _) => m,
+            Visibility::PubCrate(krate) => return def_map.krate() == krate,
             Visibility::Public => return true,
         };
         // if they're not in the same crate, it can't be visible
         if def_map.krate() != to_module.krate {
             return false;
         }
+
+        if from_module == to_module.local_id && def_map.block_id() == to_module.block {
+            // if the modules are the same, visibility is trivially satisfied
+            return true;
+        }
         Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
     }
 
@@ -90,9 +102,7 @@
                 // `to_module` is not a block, so there is no parent def map to use.
                 (None, _) => (),
                 // `to_module` is at `def_map`'s block, no need to move further.
-                (Some(a), Some(b)) if a == b => {
-                    cov_mark::hit!(is_visible_from_same_block_def_map);
-                }
+                (Some(a), Some(b)) if a == b => {}
                 _ => {
                     if let Some(parent) = to_module.def_map(db).parent() {
                         to_module = parent;
@@ -134,26 +144,56 @@
     pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
         match (self, other) {
             (_, Visibility::Public) | (Visibility::Public, _) => Some(Visibility::Public),
+            (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
+                if krate == krateb {
+                    Some(Visibility::PubCrate(krate))
+                } else {
+                    None
+                }
+            }
+            (Visibility::Module(mod_, _), Visibility::PubCrate(krate))
+            | (Visibility::PubCrate(krate), Visibility::Module(mod_, _)) => {
+                if mod_.krate == krate {
+                    Some(Visibility::PubCrate(krate))
+                } else {
+                    None
+                }
+            }
             (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
-                if mod_a.krate != mod_b.krate {
+                if mod_a == mod_b {
+                    // Most module visibilities are `pub(self)`, and assuming no errors
+                    // this will be the common and thus fast path.
+                    return Some(Visibility::Module(
+                        mod_a,
+                        match (expl_a, expl_b) {
+                            (VisibilityExplicitness::Explicit, _)
+                            | (_, VisibilityExplicitness::Explicit) => {
+                                VisibilityExplicitness::Explicit
+                            }
+                            _ => VisibilityExplicitness::Implicit,
+                        },
+                    ));
+                }
+
+                if mod_a.krate() != def_map.krate() || mod_b.krate() != def_map.krate() {
                     return None;
                 }
 
                 let def_block = def_map.block_id();
-                if (mod_a.containing_block(), mod_b.containing_block()) != (def_block, def_block) {
+                if mod_a.containing_block() != def_block || mod_b.containing_block() != def_block {
                     return None;
                 }
 
                 let mut a_ancestors =
                     iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
-                let mut b_ancestors =
-                    iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
 
                 if a_ancestors.any(|m| m == mod_b.local_id) {
                     // B is above A
                     return Some(Visibility::Module(mod_b, expl_b));
                 }
 
+                let mut b_ancestors =
+                    iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
                 if b_ancestors.any(|m| m == mod_a.local_id) {
                     // A is above B
                     return Some(Visibility::Module(mod_a, expl_a));
@@ -171,26 +211,52 @@
     pub(crate) fn min(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
         match (self, other) {
             (vis, Visibility::Public) | (Visibility::Public, vis) => Some(vis),
+            (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
+                if krate == krateb {
+                    Some(Visibility::PubCrate(krate))
+                } else {
+                    None
+                }
+            }
+            (Visibility::Module(mod_, exp), Visibility::PubCrate(krate))
+            | (Visibility::PubCrate(krate), Visibility::Module(mod_, exp)) => {
+                if mod_.krate == krate { Some(Visibility::Module(mod_, exp)) } else { None }
+            }
             (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
-                if mod_a.krate != mod_b.krate {
+                if mod_a == mod_b {
+                    // Most module visibilities are `pub(self)`, and assuming no errors
+                    // this will be the common and thus fast path.
+                    return Some(Visibility::Module(
+                        mod_a,
+                        match (expl_a, expl_b) {
+                            (VisibilityExplicitness::Explicit, _)
+                            | (_, VisibilityExplicitness::Explicit) => {
+                                VisibilityExplicitness::Explicit
+                            }
+                            _ => VisibilityExplicitness::Implicit,
+                        },
+                    ));
+                }
+
+                if mod_a.krate() != def_map.krate() || mod_b.krate() != def_map.krate() {
                     return None;
                 }
 
                 let def_block = def_map.block_id();
-                if (mod_a.containing_block(), mod_b.containing_block()) != (def_block, def_block) {
+                if mod_a.containing_block() != def_block || mod_b.containing_block() != def_block {
                     return None;
                 }
 
                 let mut a_ancestors =
                     iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
-                let mut b_ancestors =
-                    iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
 
                 if a_ancestors.any(|m| m == mod_b.local_id) {
                     // B is above A
                     return Some(Visibility::Module(mod_a, expl_a));
                 }
 
+                let mut b_ancestors =
+                    iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
                 if b_ancestors.any(|m| m == mod_a.local_id) {
                     // A is above B
                     return Some(Visibility::Module(mod_b, expl_b));
@@ -217,49 +283,62 @@
     for (field_id, field_data) in fields.iter() {
         res.insert(field_id, Visibility::resolve(db, &resolver, &field_data.visibility));
     }
+    res.shrink_to_fit();
     Arc::new(res)
 }
 
-/// Resolve visibility of a function.
-pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility {
-    let resolver = def.resolver(db);
-    let loc = def.lookup(db);
-    let tree = loc.item_tree_id().item_tree(db);
-    if let ItemContainerId::TraitId(trait_id) = loc.container {
-        trait_vis(db, &resolver, trait_id)
-    } else {
-        Visibility::resolve(db, &resolver, &tree[tree[loc.id.value].visibility])
+pub fn visibility_from_ast(
+    db: &dyn DefDatabase,
+    has_resolver: impl HasResolver,
+    ast_vis: InFile<Option<ast::Visibility>>,
+) -> Visibility {
+    let mut span_map = None;
+    let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| {
+        span_map.get_or_insert_with(|| db.span_map(ast_vis.file_id)).span_for_range(range).ctx
+    });
+    if raw_vis == RawVisibility::Public {
+        return Visibility::Public;
     }
-}
 
-/// Resolve visibility of a const.
-pub(crate) fn const_visibility_query(db: &dyn DefDatabase, def: ConstId) -> Visibility {
-    let resolver = def.resolver(db);
-    let loc = def.lookup(db);
-    let tree = loc.item_tree_id().item_tree(db);
-    if let ItemContainerId::TraitId(trait_id) = loc.container {
-        trait_vis(db, &resolver, trait_id)
-    } else {
-        Visibility::resolve(db, &resolver, &tree[tree[loc.id.value].visibility])
-    }
+    Visibility::resolve(db, &has_resolver.resolver(db), &raw_vis)
 }
 
 /// Resolve visibility of a type alias.
-pub(crate) fn type_alias_visibility_query(db: &dyn DefDatabase, def: TypeAliasId) -> Visibility {
-    let resolver = def.resolver(db);
-    let loc = def.lookup(db);
-    let tree = loc.item_tree_id().item_tree(db);
-    if let ItemContainerId::TraitId(trait_id) = loc.container {
-        trait_vis(db, &resolver, trait_id)
-    } else {
-        Visibility::resolve(db, &resolver, &tree[tree[loc.id.value].visibility])
+pub(crate) fn assoc_visibility_query(db: &dyn DefDatabase, def: AssocItemId) -> Visibility {
+    match def {
+        AssocItemId::FunctionId(function_id) => {
+            let loc = function_id.lookup(db);
+            trait_item_visibility(db, loc.container).unwrap_or_else(|| {
+                let source = loc.source(db);
+                visibility_from_ast(db, function_id, source.map(|src| src.visibility()))
+            })
+        }
+        AssocItemId::ConstId(const_id) => {
+            let loc = const_id.lookup(db);
+            trait_item_visibility(db, loc.container).unwrap_or_else(|| {
+                let source = loc.source(db);
+                visibility_from_ast(db, const_id, source.map(|src| src.visibility()))
+            })
+        }
+        AssocItemId::TypeAliasId(type_alias_id) => {
+            let loc = type_alias_id.lookup(db);
+            trait_item_visibility(db, loc.container).unwrap_or_else(|| {
+                let source = loc.source(db);
+                visibility_from_ast(db, type_alias_id, source.map(|src| src.visibility()))
+            })
+        }
     }
 }
 
-#[inline]
-fn trait_vis(db: &dyn DefDatabase, resolver: &Resolver<'_>, trait_id: TraitId) -> Visibility {
-    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];
-    Visibility::resolve(db, resolver, &item_tree[tr_def.visibility])
+fn trait_item_visibility(db: &dyn DefDatabase, container: ItemContainerId) -> Option<Visibility> {
+    match container {
+        ItemContainerId::TraitId(trait_) => Some(trait_visibility(db, trait_)),
+        _ => None,
+    }
+}
+
+fn trait_visibility(db: &dyn DefDatabase, def: TraitId) -> Visibility {
+    let loc = def.lookup(db);
+    let source = loc.source(db);
+    visibility_from_ast(db, def, source.map(|src| src.visibility()))
 }
diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs
index 62b7b63..d5874f8 100644
--- a/crates/hir-expand/src/builtin/quote.rs
+++ b/crates/hir-expand/src/builtin/quote.rs
@@ -277,8 +277,8 @@
         assert_eq!(quoted.to_string(), "hello");
         let t = format!("{quoted:#?}");
         expect![[r#"
-            SUBTREE $$ 937550:0@0..0#ROOT2024 937550:0@0..0#ROOT2024
-              IDENT   hello 937550:0@0..0#ROOT2024"#]]
+            SUBTREE $$ 937550:Root[0000, 0]@0..0#ROOT2024 937550:Root[0000, 0]@0..0#ROOT2024
+              IDENT   hello 937550:Root[0000, 0]@0..0#ROOT2024"#]]
         .assert_eq(&t);
     }
 
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 7cb1b6c..7e9928c 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -13,7 +13,7 @@
     AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
     EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId,
     MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
-    attrs::{AttrId, collect_attrs},
+    attrs::{AttrId, AttrInput, RawAttrs, collect_attrs},
     builtin::pseudo_derive_attr_expansion,
     cfg_process,
     declarative::DeclarativeMacroExpander,
@@ -60,6 +60,7 @@
     fn proc_macros_for_crate(&self, krate: Crate) -> Option<Arc<CrateProcMacros>>;
 
     #[salsa::invoke(ast_id_map)]
+    #[salsa::lru(1024)]
     fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
 
     #[salsa::transparent]
@@ -241,30 +242,36 @@
 
     let attr_arg = match loc.kind {
         MacroCallKind::Attr { invoc_attr_index, .. } => {
-            let attr = if loc.def.is_attribute_derive() {
+            if loc.def.is_attribute_derive() {
                 // for pseudo-derive expansion we actually pass the attribute itself only
-                ast::Attr::cast(speculative_args.clone())
+                ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map(
+                    |token_tree| {
+                        let mut tree = syntax_node_to_token_tree(
+                            token_tree.syntax(),
+                            span_map,
+                            span,
+                            DocCommentDesugarMode::ProcMacro,
+                        );
+                        *tree.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span);
+                        tree
+                    },
+                )
             } else {
                 // Attributes may have an input token tree, build the subtree and map for this as well
                 // then try finding a token id for our token if it is inside this input subtree.
                 let item = ast::Item::cast(speculative_args.clone())?;
-                collect_attrs(&item)
-                    .nth(invoc_attr_index.ast_index())
-                    .and_then(|x| Either::left(x.1))
-            }?;
-            match attr.token_tree() {
-                Some(token_tree) => {
-                    let mut tree = syntax_node_to_token_tree(
-                        token_tree.syntax(),
-                        span_map,
-                        span,
-                        DocCommentDesugarMode::ProcMacro,
-                    );
-                    *tree.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span);
-
-                    Some(tree)
-                }
-                _ => None,
+                let attrs = RawAttrs::new_expanded(db, &item, span_map, loc.krate.cfg_options(db));
+                attrs.iter().find(|attr| attr.id == invoc_attr_index).and_then(|attr| {
+                    match attr.input.as_deref()? {
+                        AttrInput::TokenTree(tt) => {
+                            let mut attr_arg = tt.clone();
+                            attr_arg.top_subtree_delimiter_mut().kind =
+                                tt::DelimiterKind::Invisible;
+                            Some(attr_arg)
+                        }
+                        AttrInput::Literal(_) => None,
+                    }
+                })
             }
         }
         _ => None,
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 8024823..a73a223 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -106,7 +106,7 @@
 /// It is stable across reparses, and can be used as salsa key/value.
 pub type AstId<N> = crate::InFile<FileAstId<N>>;
 
-impl<N: AstIdNode> AstId<N> {
+impl<N: AstNode> AstId<N> {
     pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
         self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
     }
@@ -122,6 +122,13 @@
     pub fn erase(&self) -> ErasedAstId {
         crate::InFile::new(self.file_id, self.value.erase())
     }
+    #[inline]
+    pub fn upcast<M: AstIdNode>(self) -> AstId<M>
+    where
+        N: Into<M>,
+    {
+        self.map(|it| it.upcast())
+    }
 }
 
 pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
diff --git a/crates/hir-expand/src/inert_attr_macro.rs b/crates/hir-expand/src/inert_attr_macro.rs
index 543ac06..385c98e 100644
--- a/crates/hir-expand/src/inert_attr_macro.rs
+++ b/crates/hir-expand/src/inert_attr_macro.rs
@@ -486,7 +486,7 @@
         rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing,
         INTERNAL_UNSTABLE
     ),
-    // Do not const-check this function's body. It will always get replaced during CTFE.
+    // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`.
     rustc_attr!(
         rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE
     ),
diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs
index 1cd975b..1c8ebb6 100644
--- a/crates/hir-expand/src/proc_macro.rs
+++ b/crates/hir-expand/src/proc_macro.rs
@@ -34,9 +34,7 @@
         current_dir: String,
     ) -> Result<tt::TopSubtree, ProcMacroExpansionError>;
 
-    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
-        other.type_id() == self.type_id()
-    }
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool;
 }
 
 impl PartialEq for dyn ProcMacroExpander {
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index efa544c..8b65126 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -31,7 +31,7 @@
 triomphe.workspace = true
 typed-arena = "2.0.2"
 indexmap.workspace = true
-rustc_apfloat = "0.2.2"
+rustc_apfloat = "0.2.3"
 query-group.workspace = true
 salsa.workspace = true
 salsa-macros.workspace = true
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index 22b96b5..7945442 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -63,7 +63,7 @@
     ) -> Option<rust_ir::AssociatedTyValueId<Interner>> {
         let alias_id = from_assoc_type_id(assoc_type_id);
         let trait_sig = self.db.type_alias_signature(alias_id);
-        self.db.impl_items(hir_def::ImplId::from_chalk(self.db, impl_id)).items.iter().find_map(
+        hir_def::ImplId::from_chalk(self.db, impl_id).impl_items(self.db).items.iter().find_map(
             |(name, item)| match item {
                 AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => {
                     Some(TypeAliasAsValue(*alias).to_chalk(self.db))
@@ -261,10 +261,20 @@
         &self,
         well_known_trait: WellKnownTrait,
     ) -> Option<chalk_ir::TraitId<Interner>> {
-        let lang_attr = lang_item_from_well_known_trait(well_known_trait);
-        let trait_ = lang_attr.resolve_trait(self.db, self.krate)?;
+        let lang_item = lang_item_from_well_known_trait(well_known_trait);
+        let trait_ = lang_item.resolve_trait(self.db, self.krate)?;
         Some(to_chalk_trait_id(trait_))
     }
+    fn well_known_assoc_type_id(
+        &self,
+        assoc_type: rust_ir::WellKnownAssocType,
+    ) -> Option<chalk_ir::AssocTypeId<Interner>> {
+        let lang_item = match assoc_type {
+            rust_ir::WellKnownAssocType::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput,
+        };
+        let alias = lang_item.resolve_type_alias(self.db, self.krate)?;
+        Some(to_assoc_type_id(alias))
+    }
 
     fn program_clauses_for_env(
         &self,
@@ -813,11 +823,11 @@
             (rust_ir::AdtKind::Struct, vec![variant_id_to_fields(id.into())])
         }
         hir_def::AdtId::EnumId(id) => {
-            let variants = db
-                .enum_variants(id)
+            let variants = id
+                .enum_variants(db)
                 .variants
                 .iter()
-                .map(|&(variant_id, _)| variant_id_to_fields(variant_id.into()))
+                .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into()))
                 .collect();
             (rust_ir::AdtKind::Enum, variants)
         }
@@ -870,8 +880,8 @@
 
     let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses };
     let trait_data = db.trait_items(trait_);
-    let associated_ty_value_ids = db
-        .impl_items(impl_id)
+    let associated_ty_value_ids = impl_id
+        .impl_items(db)
         .items
         .iter()
         .filter_map(|(_, item)| match item {
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index aabc4c4..836cc96 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -16,7 +16,8 @@
     ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
     QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
     db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
-    from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst,
+    from_placeholder_idx, generics::generics, mapping::ToChalk, to_chalk_trait_id,
+    utils::ClosureSubst,
 };
 
 pub trait TyExt {
@@ -190,10 +191,9 @@
     fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
         match *self.kind(Interner) {
             TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
-            TyKind::FnDef(callable, ..) => Some(GenericDefId::from_callable(
-                db,
-                db.lookup_intern_callable_def(callable.into()),
-            )),
+            TyKind::FnDef(callable, ..) => {
+                Some(GenericDefId::from_callable(db, ToChalk::from_chalk(db, callable)))
+            }
             TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
             TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
             _ => None,
@@ -202,7 +202,7 @@
 
     fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
         match self.kind(Interner) {
-            &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
+            &TyKind::FnDef(def, ..) => Some(ToChalk::from_chalk(db, def)),
             _ => None,
         }
     }
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index f903b06..24530a5 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -286,7 +286,7 @@
         let value = match prev_idx {
             Some(prev_idx) => {
                 1 + db.const_eval_discriminant(
-                    db.enum_variants(loc.parent).variants[prev_idx as usize].0,
+                    loc.parent.enum_variants(db).variants[prev_idx as usize].0,
                 )?
             }
             _ => 0,
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index ee375d6..5e85978 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -112,16 +112,16 @@
 }
 
 #[test]
-fn min_align_of_val() {
+fn align_of_val() {
     check_number(
         r#"
         //- minicore: coerce_unsized
         #[rustc_intrinsic]
-        pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
+        pub fn align_of_val<T: ?Sized>(_: *const T) -> usize;
 
         struct X(i32, u8);
 
-        const GOAL: usize = min_align_of_val(&X(1, 2));
+        const GOAL: usize = align_of_val(&X(1, 2));
         "#,
         4,
     );
@@ -129,11 +129,11 @@
         r#"
         //- minicore: coerce_unsized
         #[rustc_intrinsic]
-        pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
+        pub fn align_of_val<T: ?Sized>(_: *const T) -> usize;
 
         const GOAL: usize = {
             let x: &[i32] = &[1, 2, 3];
-            min_align_of_val(x)
+            align_of_val(x)
         };
         "#,
         4,
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 1e985dc..1029969 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -237,9 +237,6 @@
 
     // Interned IDs for Chalk integration
     #[salsa::interned]
-    fn intern_callable_def(&self, callable_def: CallableDefId) -> InternedCallableDefId;
-
-    #[salsa::interned]
     fn intern_type_or_const_param_id(
         &self,
         param_id: TypeOrConstParamId,
@@ -347,7 +344,3 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId);
 impl_intern_key!(InternedCoroutineId, InternedCoroutine);
-
-// This exists just for Chalk, because Chalk just has a single `FnDefId` where
-// we have different IDs for struct and enum variant constructors.
-impl_intern_key!(InternedCallableDefId, CallableDefId);
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 099100a..1873f12 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -395,9 +395,9 @@
 
     /// Check incorrect names for enum variants.
     fn validate_enum_variants(&mut self, enum_id: EnumId) {
-        let data = self.db.enum_variants(enum_id);
+        let data = enum_id.enum_variants(self.db);
 
-        for (variant_id, _) in data.variants.iter() {
+        for (variant_id, _, _) in data.variants.iter() {
             self.validate_enum_variant_fields(*variant_id);
         }
 
@@ -405,7 +405,7 @@
         let mut enum_variants_replacements = data
             .variants
             .iter()
-            .filter_map(|(_, name)| {
+            .filter_map(|(_, name, _)| {
                 to_camel_case(&name.display_no_db(edition).to_smolstr()).map(|new_name| {
                     Replacement {
                         current_name: name.clone(),
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 9eb7ffe..df2eb41 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -642,7 +642,7 @@
     }
 
     let non_empty_enum = match scrut_ty.as_adt() {
-        Some((AdtId::EnumId(e), _)) => !cx.db.enum_variants(e).variants.is_empty(),
+        Some((AdtId::EnumId(e), _)) => !e.enum_variants(cx.db).variants.is_empty(),
         _ => false,
     };
     let display_target = DisplayTarget::from_crate(cx.db, krate);
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index 7df22a4..916876d 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -328,7 +328,7 @@
                             write!(
                                 f,
                                 "{}",
-                                f.db.enum_variants(loc.parent).variants[loc.index as usize]
+                                loc.parent.enum_variants(f.db).variants[loc.index as usize]
                                     .1
                                     .display(f.db, f.edition())
                             )?;
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index dd82a0f..2873a3e 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -50,7 +50,7 @@
     }
 
     fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId {
-        db.enum_variants(eid).variants[self.0].0
+        eid.enum_variants(db).variants[self.0].0
     }
 }
 
@@ -458,14 +458,14 @@
             TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
             TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
             &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
-                let enum_data = cx.db.enum_variants(enum_id);
+                let enum_data = enum_id.enum_variants(cx.db);
                 let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
 
                 if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
                     ConstructorSet::NoConstructors
                 } else {
                     let mut variants = IndexVec::with_capacity(enum_data.variants.len());
-                    for &(variant, _) in enum_data.variants.iter() {
+                    for &(variant, _, _) in enum_data.variants.iter() {
                         let is_uninhabited = is_enum_variant_uninhabited_from(
                             cx.db,
                             variant,
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index f210dd8..1aa7e0f 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -914,7 +914,7 @@
                     write!(
                         f,
                         "{}",
-                        f.db.enum_variants(loc.parent).variants[loc.index as usize]
+                        loc.parent.enum_variants(f.db).variants[loc.index as usize]
                             .1
                             .display(f.db, f.edition())
                     )?;
@@ -1208,7 +1208,7 @@
                         write!(
                             f,
                             "{}",
-                            db.enum_variants(loc.parent).variants[loc.index as usize]
+                            loc.parent.enum_variants(db).variants[loc.index as usize]
                                 .1
                                 .display(db, f.edition())
                         )?
@@ -2082,6 +2082,7 @@
 ) -> Result<(), HirDisplayError> {
     match vis {
         Visibility::Public => write!(f, "pub "),
+        Visibility::PubCrate(_) => write!(f, "pub(crate) "),
         Visibility::Module(vis_id, _) => {
             let def_map = module_id.def_map(f.db);
             let root_module_id = def_map.module_id(DefMap::ROOT);
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index 7076375..5577be8 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -67,11 +67,11 @@
                 }
                 // Unions cannot have fields with destructors.
                 AdtId::UnionId(_) => DropGlue::None,
-                AdtId::EnumId(id) => db
-                    .enum_variants(id)
+                AdtId::EnumId(id) => id
+                    .enum_variants(db)
                     .variants
                     .iter()
-                    .map(|&(variant, _)| {
+                    .map(|&(variant, _, _)| {
                         db.field_types(variant.into())
                             .iter()
                             .map(|(_, field_ty)| {
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index ed8d8dc..4809494 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -122,7 +122,7 @@
     res
 }
 
-fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool {
+pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool {
     let krate = def.module(db).krate();
     let Some(sized) = LangItem::Sized.resolve_trait(db, krate) else {
         return false;
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index bb4aaf7..a3ed399 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -229,7 +229,7 @@
     }
 
     /// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`).
-    pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution {
+    pub fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution {
         Substitution::from_iter(
             Interner,
             self.iter_id().map(|id| match id {
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 14eb716..80478f1 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -1673,7 +1673,7 @@
                     // If we can resolve to an enum variant, it takes priority over associated type
                     // of the same name.
                     if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
-                        let enum_data = self.db.enum_variants(id);
+                        let enum_data = id.enum_variants(self.db);
                         if let Some(variant) = enum_data.variant(current_segment.name) {
                             return if remaining_segments.len() == 1 {
                                 (ty, Some(variant.into()))
@@ -1792,7 +1792,7 @@
                 let segment = path.segments().last().unwrap();
                 // this could be an enum variant or associated type
                 if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
-                    let enum_data = self.db.enum_variants(enum_id);
+                    let enum_data = enum_id.enum_variants(self.db);
                     if let Some(variant) = enum_data.variant(segment) {
                         return (ty, Some(variant.into()));
                     }
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs
index 10d8579..8d345de 100644
--- a/crates/hir-ty/src/infer/cast.rs
+++ b/crates/hir-ty/src/infer/cast.rs
@@ -43,7 +43,7 @@
                 let (AdtId::EnumId(id), _) = t.as_adt()? else {
                     return None;
                 };
-                let enum_data = table.db.enum_variants(id);
+                let enum_data = id.enum_variants(table.db);
                 if enum_data.is_payload_free(table.db) { Some(Self::Int(Int::CEnum)) } else { None }
             }
             TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)),
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index bd57ca8..d1432ca 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -1360,7 +1360,7 @@
                 if let Some(variant) = self.result.variant_resolution_for_pat(p) {
                     let adt = variant.adt_id(self.db);
                     let is_multivariant = match adt {
-                        hir_def::AdtId::EnumId(e) => self.db.enum_variants(e).variants.len() != 1,
+                        hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1,
                         _ => false,
                     };
                     if is_multivariant {
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 9d4bbe5..c327c13 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -397,7 +397,7 @@
             Some((AdtId::EnumId(e), subst)) => (e, subst),
             _ => return None,
         };
-        let enum_data = self.db.enum_variants(enum_id);
+        let enum_data = enum_id.enum_variants(self.db);
         let variant = enum_data.variant(name)?;
         self.write_variant_resolution(id, variant.into());
         Some((ValueNs::EnumVariantId(variant), subst.clone()))
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index e81a5e3..79a99321 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -113,9 +113,9 @@
             AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
             AdtId::StructId(s) => self.visit_variant(s.into(), subst),
             AdtId::EnumId(e) => {
-                let enum_data = self.db.enum_variants(e);
+                let enum_data = e.enum_variants(self.db);
 
-                for &(variant, _) in enum_data.variants.iter() {
+                for &(variant, _, _) in enum_data.variants.iter() {
                     let variant_inhabitedness = self.visit_variant(variant.into(), subst);
                     match variant_inhabitedness {
                         Break(VisiblyUninhabited) => (),
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 3a020bf..dff986f 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -56,11 +56,11 @@
             (r, data.repr.unwrap_or_default(), false)
         }
         AdtId::EnumId(e) => {
-            let variants = db.enum_variants(e);
+            let variants = e.enum_variants(db);
             let r = variants
                 .variants
                 .iter()
-                .map(|&(v, _)| handle_variant(v.into(), &db.variant_fields(v.into())))
+                .map(|&(v, _, _)| handle_variant(v.into(), &db.variant_fields(v.into())))
                 .collect::<Result<SmallVec<_>, _>>()?;
             (r, db.enum_signature(e).repr.unwrap_or_default(), false)
         }
@@ -82,7 +82,7 @@
             |min, max| repr_discr(dl, &repr, min, max).unwrap_or((Integer::I8, false)),
             variants.iter_enumerated().filter_map(|(id, _)| {
                 let AdtId::EnumId(e) = def else { return None };
-                let d = db.const_eval_discriminant(db.enum_variants(e).variants[id.0].0).ok()?;
+                let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?;
                 Some((id, d))
             }),
             // FIXME: The current code for niche-filling relies on variant indices
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 128569d..148f2a4 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -98,7 +98,7 @@
     ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*,
 };
 pub use mapping::{
-    from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
+    ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
     lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id,
     to_foreign_def_id, to_placeholder_idx,
 };
@@ -542,7 +542,7 @@
     }
 
     pub fn from_def(db: &dyn HirDatabase, def: FnDefId, substs: &Substitution) -> CallableSig {
-        let callable_def = db.lookup_intern_callable_def(def.into());
+        let callable_def = ToChalk::from_chalk(db, def);
         let sig = db.callable_item_signature(callable_def);
         sig.substitute(Interner, substs)
     }
diff --git a/crates/hir-ty/src/mapping.rs b/crates/hir-ty/src/mapping.rs
index 2abc1ac..6936d81 100644
--- a/crates/hir-ty/src/mapping.rs
+++ b/crates/hir-ty/src/mapping.rs
@@ -16,7 +16,7 @@
     PlaceholderIndex, chalk_db, db::HirDatabase,
 };
 
-pub(crate) trait ToChalk {
+pub trait ToChalk {
     type Chalk;
     fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk;
     fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self;
@@ -44,12 +44,12 @@
 impl ToChalk for CallableDefId {
     type Chalk = FnDefId;
 
-    fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
-        db.intern_callable_def(self).into()
+    fn to_chalk(self, _db: &dyn HirDatabase) -> FnDefId {
+        chalk_ir::FnDefId(salsa::plumbing::AsId::as_id(&self))
     }
 
     fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId {
-        db.lookup_intern_callable_def(fn_def_id.into())
+        salsa::plumbing::FromIdWithDb::from_id(fn_def_id.0, db.zalsa())
     }
 }
 
@@ -70,18 +70,6 @@
     }
 }
 
-impl From<FnDefId> for crate::db::InternedCallableDefId {
-    fn from(fn_def_id: FnDefId) -> Self {
-        Self::from_id(fn_def_id.0)
-    }
-}
-
-impl From<crate::db::InternedCallableDefId> for FnDefId {
-    fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self {
-        chalk_ir::FnDefId(callable_def_id.as_id())
-    }
-}
-
 impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
     fn from(id: OpaqueTyId) -> Self {
         FromId::from_id(id.0)
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 3b295d4..25f1782 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -790,7 +790,7 @@
     mut impls: impl Iterator<Item = ImplId>,
     mut table: InferenceTable<'_>,
     actual_trait_ref: TraitRef,
-) -> Option<(Arc<ImplItems>, Substitution)> {
+) -> Option<(&ImplItems, Substitution)> {
     let db = table.db;
     impls.find_map(|impl_| {
         table.run_in_snapshot(|table| {
@@ -811,7 +811,7 @@
             let goal = crate::Goal::all(Interner, wcs);
             table.try_obligation(goal.clone())?;
             table.register_obligation(goal);
-            Some((db.impl_items(impl_), table.resolve_completely(impl_substs)))
+            Some((impl_.impl_items(db), table.resolve_completely(impl_substs)))
         })
     })
 }
@@ -875,7 +875,7 @@
 
             _ => false,
         };
-        let items = db.impl_items(impl_id);
+        let items = impl_id.impl_items(db);
         rustc_has_incoherent_inherent_impls
             && !items.items.is_empty()
             && items.items.iter().all(|&(_, assoc)| match assoc {
@@ -1462,7 +1462,7 @@
         callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
     ) -> ControlFlow<()> {
         for &impl_id in impls.for_self_ty(self_ty) {
-            for &(ref item_name, item) in table.db.impl_items(impl_id).items.iter() {
+            for &(ref item_name, item) in impl_id.impl_items(table.db).items.iter() {
                 let visible = match is_valid_impl_method_candidate(
                     table,
                     self_ty,
@@ -1550,7 +1550,7 @@
             check_that!(name.is_none_or(|n| n == item_name));
 
             if let Some(from_module) = visible_from_module {
-                if !db.const_visibility(c).is_visible_from(db, from_module) {
+                if !db.assoc_visibility(c.into()).is_visible_from(db, from_module) {
                     cov_mark::hit!(const_candidate_not_visible);
                     return IsValidCandidate::NotVisible;
                 }
@@ -1639,7 +1639,7 @@
     let data = db.function_signature(fn_id);
 
     if let Some(from_module) = visible_from_module {
-        if !db.function_visibility(fn_id).is_visible_from(db, from_module) {
+        if !db.assoc_visibility(fn_id.into()).is_visible_from(db, from_module) {
             cov_mark::hit!(autoderef_candidate_not_visible);
             return IsValidCandidate::NotVisible;
         }
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 21e5428..a8156ec 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -32,7 +32,7 @@
 
 use crate::{
     CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, Interner,
-    MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+    MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
     consteval::{ConstEvalError, intern_const_scalar, try_const_usize},
     db::{HirDatabase, InternedClosure},
     display::{ClosureStyle, DisplayTarget, HirDisplay},
@@ -1631,7 +1631,7 @@
             Variants::Empty => unreachable!(),
             Variants::Single { index } => {
                 let r =
-                    self.const_eval_discriminant(self.db.enum_variants(e).variants[index.0].0)?;
+                    self.const_eval_discriminant(e.enum_variants(self.db).variants[index.0].0)?;
                 Ok(r)
             }
             Variants::Multiple { tag, tag_encoding, variants, .. } => {
@@ -1656,7 +1656,7 @@
                             .unwrap_or(*untagged_variant)
                             .0;
                         let result =
-                            self.const_eval_discriminant(self.db.enum_variants(e).variants[idx].0)?;
+                            self.const_eval_discriminant(e.enum_variants(self.db).variants[idx].0)?;
                         Ok(result)
                     }
                 }
@@ -2771,12 +2771,15 @@
             Err(e) => {
                 let db = self.db;
                 let loc = variant.lookup(db);
-                let enum_loc = loc.parent.lookup(db);
                 let edition = self.crate_id.data(self.db).edition;
                 let name = format!(
                     "{}::{}",
-                    enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition),
-                    loc.id.item_tree(db)[loc.id.value].name.display(db, edition),
+                    self.db.enum_signature(loc.parent).name.display(db, edition),
+                    loc.parent
+                        .enum_variants(self.db)
+                        .variant_name_by_id(variant)
+                        .unwrap()
+                        .display(db, edition),
                 );
                 Err(MirEvalError::ConstEvalError(name, Box::new(e)))
             }
@@ -2927,7 +2930,7 @@
     let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size())?;
     evaluator.write_memory(a2, &data.addr.to_bytes())?;
     let debug_fmt_fn_ptr = evaluator.vtable_map.id(TyKind::FnDef(
-        db.intern_callable_def(debug_fmt_fn.into()).into(),
+        CallableDefId::FunctionId(debug_fmt_fn).to_chalk(db),
         Substitution::from1(Interner, c.data(Interner).ty.clone()),
     )
     .intern(Interner));
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 90c52ee..6ebde01 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -65,9 +65,7 @@
                 Some(abi) => *abi == sym::rust_dash_intrinsic,
                 None => match def.lookup(self.db).container {
                     hir_def::ItemContainerId::ExternBlockId(block) => {
-                        let id = block.lookup(self.db).id;
-                        id.item_tree(self.db)[id.value].abi.as_ref()
-                            == Some(&sym::rust_dash_intrinsic)
+                        block.abi(self.db) == Some(sym::rust_dash_intrinsic)
                     }
                     _ => false,
                 },
@@ -86,10 +84,7 @@
             );
         }
         let is_extern_c = match def.lookup(self.db).container {
-            hir_def::ItemContainerId::ExternBlockId(block) => {
-                let id = block.lookup(self.db).id;
-                id.item_tree(self.db)[id.value].abi.as_ref() == Some(&sym::C)
-            }
+            hir_def::ItemContainerId::ExternBlockId(block) => block.abi(self.db) == Some(sym::C),
             _ => false,
         };
         if is_extern_c {
@@ -764,7 +759,9 @@
                 let size = self.size_of_sized(ty, locals, "size_of arg")?;
                 destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
             }
-            "min_align_of" | "pref_align_of" => {
+            // FIXME: `min_align_of` was renamed to `align_of` in Rust 1.89
+            // (https://github.com/rust-lang/rust/pull/142410)
+            "min_align_of" | "align_of" => {
                 let Some(ty) =
                     generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
                 else {
@@ -796,17 +793,19 @@
                     destination.write_from_bytes(self, &size.to_le_bytes())
                 }
             }
-            "min_align_of_val" => {
+            // FIXME: `min_align_of_val` was renamed to `align_of_val` in Rust 1.89
+            // (https://github.com/rust-lang/rust/pull/142410)
+            "min_align_of_val" | "align_of_val" => {
                 let Some(ty) =
                     generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
                 else {
                     return Err(MirEvalError::InternalError(
-                        "min_align_of_val generic arg is not provided".into(),
+                        "align_of_val generic arg is not provided".into(),
                     ));
                 };
                 let [arg] = args else {
                     return Err(MirEvalError::InternalError(
-                        "min_align_of_val args are not provided".into(),
+                        "align_of_val args are not provided".into(),
                     ));
                 };
                 if let Some((_, align)) = self.size_align_of(ty, locals)? {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 99d9351..71e038b 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1922,11 +1922,14 @@
                 let edition = self.edition();
                 let db = self.db;
                 let loc = variant.lookup(db);
-                let enum_loc = loc.parent.lookup(db);
                 let name = format!(
                     "{}::{}",
-                    enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition),
-                    loc.id.item_tree(db)[loc.id.value].name.display(db, edition),
+                    self.db.enum_signature(loc.parent).name.display(db, edition),
+                    loc.parent
+                        .enum_variants(self.db)
+                        .variant_name_by_id(variant)
+                        .unwrap()
+                        .display(db, edition),
                 );
                 Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))
             }
@@ -2152,7 +2155,7 @@
             .to_string(),
         DefWithBodyId::VariantId(it) => {
             let loc = it.lookup(db);
-            db.enum_variants(loc.parent).variants[loc.index as usize]
+            loc.parent.enum_variants(db).variants[loc.index as usize]
                 .1
                 .display(db, edition)
                 .to_string()
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index c22bada..ad66469 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -297,11 +297,8 @@
         let result_ref = TyKind::Ref(mutability, error_lifetime(), result_ty).intern(Interner);
         let mut result: Place = self.temp(result_ref, current, span)?.into();
         let index_fn_op = Operand::const_zst(
-            TyKind::FnDef(
-                self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
-                index_fn.1,
-            )
-            .intern(Interner),
+            TyKind::FnDef(CallableDefId::FunctionId(index_fn.0).to_chalk(self.db), index_fn.1)
+                .intern(Interner),
         );
         let Some(current) = self.lower_call(
             index_fn_op,
@@ -357,7 +354,7 @@
             .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
         let deref_fn_op = Operand::const_zst(
             TyKind::FnDef(
-                self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(),
+                CallableDefId::FunctionId(deref_fn).to_chalk(self.db),
                 Substitution::from1(Interner, source_ty),
             )
             .intern(Interner),
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 7ae6e90..8764e48 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -63,16 +63,16 @@
             }
             hir_def::DefWithBodyId::VariantId(id) => {
                 let loc = id.lookup(db);
-                let enum_loc = loc.parent.lookup(db);
+                let edition = this.display_target.edition;
                 w!(
                     this,
                     "enum {}::{} = ",
-                    enum_loc.id.item_tree(db)[enum_loc.id.value]
-                        .name
-                        .display(db, this.display_target.edition),
-                    loc.id.item_tree(db)[loc.id.value]
-                        .name
-                        .display(db, this.display_target.edition),
+                    db.enum_signature(loc.parent).name.display(db, edition),
+                    loc.parent
+                        .enum_variants(db)
+                        .variant_name_by_id(id)
+                        .unwrap()
+                        .display(db, edition),
                 )
             }
         });
@@ -336,7 +336,7 @@
                             w!(
                                 this,
                                 " as {}).{}",
-                                this.db.enum_variants(loc.parent).variants[loc.index as usize]
+                                loc.parent.enum_variants(this.db).variants[loc.index as usize]
                                     .1
                                     .display(this.db, this.display_target.edition),
                                 name.display(this.db, this.display_target.edition)
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 2b75bd6..9ca6ee4 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -437,7 +437,7 @@
 ) {
     visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb);
     for impl_id in crate_def_map[module_id].scope.impls() {
-        let impl_data = db.impl_items(impl_id);
+        let impl_data = impl_id.impl_items(db);
         for &(_, item) in impl_data.items.iter() {
             match item {
                 AssocItemId::FunctionId(it) => {
@@ -479,7 +479,7 @@
                     visit_body(db, &body, cb);
                 }
                 ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => {
-                    db.enum_variants(it).variants.iter().for_each(|&(it, _)| {
+                    it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| {
                         let body = db.body(it.into());
                         cb(it.into());
                         visit_body(db, &body, cb);
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index e8e3812..905fd8a 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -1,5 +1,6 @@
 use base_db::SourceDatabase;
-use hir_def::ModuleDefId;
+use expect_test::Expect;
+use hir_def::{DefWithBodyId, ModuleDefId};
 use test_fixture::WithFixture;
 
 use crate::{db::HirDatabase, test_db::TestDB};
@@ -15,8 +16,9 @@
     $01 + 1
 }",
     );
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let crate_def_map = module.def_map(&db);
             visit_module(&db, crate_def_map, module.local_id, &mut |def| {
@@ -24,9 +26,31 @@
                     db.infer(it.into());
                 }
             });
-        });
-        assert!(format!("{events:?}").contains("infer_shim"))
-    }
+        },
+        &[("infer_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "infer_shim",
+                "function_signature_shim",
+                "function_signature_with_source_map_shim",
+                "attrs_shim",
+                "body_shim",
+                "body_with_source_map_shim",
+                "trait_environment_shim",
+                "return_type_impl_traits_shim",
+                "expr_scopes_shim",
+                "lang_item",
+                "crate_lang_items",
+                "lang_item",
+            ]
+        "#]],
+    );
 
     let new_text = "
 fn foo() -> i32 {
@@ -37,8 +61,9 @@
 
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let crate_def_map = module.def_map(&db);
             visit_module(&db, crate_def_map, module.local_id, &mut |def| {
@@ -46,9 +71,22 @@
                     db.infer(it.into());
                 }
             });
-        });
-        assert!(!format!("{events:?}").contains("infer_shim"), "{events:#?}")
-    }
+        },
+        &[("infer_shim", 0)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "attrs_shim",
+                "function_signature_with_source_map_shim",
+                "function_signature_shim",
+                "body_with_source_map_shim",
+                "body_shim",
+            ]
+        "#]],
+    );
 }
 
 #[test]
@@ -66,8 +104,9 @@
     1 + 1
 }",
     );
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let crate_def_map = module.def_map(&db);
             visit_module(&db, crate_def_map, module.local_id, &mut |def| {
@@ -75,9 +114,49 @@
                     db.infer(it.into());
                 }
             });
-        });
-        assert!(format!("{events:?}").contains("infer_shim"))
-    }
+        },
+        &[("infer_shim", 3)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "infer_shim",
+                "function_signature_shim",
+                "function_signature_with_source_map_shim",
+                "attrs_shim",
+                "body_shim",
+                "body_with_source_map_shim",
+                "trait_environment_shim",
+                "return_type_impl_traits_shim",
+                "expr_scopes_shim",
+                "lang_item",
+                "crate_lang_items",
+                "attrs_shim",
+                "attrs_shim",
+                "lang_item",
+                "infer_shim",
+                "function_signature_shim",
+                "function_signature_with_source_map_shim",
+                "body_shim",
+                "body_with_source_map_shim",
+                "trait_environment_shim",
+                "return_type_impl_traits_shim",
+                "expr_scopes_shim",
+                "infer_shim",
+                "function_signature_shim",
+                "function_signature_with_source_map_shim",
+                "body_shim",
+                "body_with_source_map_shim",
+                "trait_environment_shim",
+                "return_type_impl_traits_shim",
+                "expr_scopes_shim",
+            ]
+        "#]],
+    );
 
     let new_text = "
 fn foo() -> f32 {
@@ -93,8 +172,9 @@
 
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let crate_def_map = module.def_map(&db);
             visit_module(&db, crate_def_map, module.local_id, &mut |def| {
@@ -102,9 +182,34 @@
                     db.infer(it.into());
                 }
             });
-        });
-        assert_eq!(format!("{events:?}").matches("infer_shim").count(), 1, "{events:#?}")
-    }
+        },
+        &[("infer_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "attrs_shim",
+                "function_signature_with_source_map_shim",
+                "function_signature_shim",
+                "body_with_source_map_shim",
+                "body_shim",
+                "attrs_shim",
+                "attrs_shim",
+                "function_signature_with_source_map_shim",
+                "function_signature_shim",
+                "body_with_source_map_shim",
+                "body_shim",
+                "infer_shim",
+                "expr_scopes_shim",
+                "function_signature_with_source_map_shim",
+                "function_signature_shim",
+                "body_with_source_map_shim",
+                "body_shim",
+            ]
+        "#]],
+    );
 }
 
 #[test]
@@ -121,14 +226,26 @@
 }
 $0",
     );
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 
     let new_text = "
 fn foo() -> i32 {
@@ -146,24 +263,25 @@
 
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let actual = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-
-        let expected = vec![
-            "parse_shim".to_owned(),
-            "ast_id_map_shim".to_owned(),
-            "file_item_tree_shim".to_owned(),
-            "real_span_map_shim".to_owned(),
-            "crate_local_def_map".to_owned(),
-            "trait_impls_in_crate_shim".to_owned(),
-        ];
-
-        assert_eq!(expected, actual);
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "crate_local_def_map",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 }
 
 #[test]
@@ -180,14 +298,26 @@
 }
 $0",
     );
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 
     let new_text = "
 fn foo() -> i32 {
@@ -206,24 +336,25 @@
 
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let actual = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-
-        let expected = vec![
-            "parse_shim".to_owned(),
-            "ast_id_map_shim".to_owned(),
-            "file_item_tree_shim".to_owned(),
-            "real_span_map_shim".to_owned(),
-            "crate_local_def_map".to_owned(),
-            "trait_impls_in_crate_shim".to_owned(),
-        ];
-
-        assert_eq!(expected, actual);
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "crate_local_def_map",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 }
 
 #[test]
@@ -240,14 +371,26 @@
 }
 $0",
     );
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 
     let new_text = "
 use std::collections::HashMap;
@@ -263,24 +406,25 @@
 
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let actual = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-
-        let expected = vec![
-            "parse_shim".to_owned(),
-            "ast_id_map_shim".to_owned(),
-            "file_item_tree_shim".to_owned(),
-            "real_span_map_shim".to_owned(),
-            "crate_local_def_map".to_owned(),
-            "trait_impls_in_crate_shim".to_owned(),
-        ];
-
-        assert_eq!(expected, actual);
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "crate_local_def_map",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 }
 
 #[test]
@@ -301,14 +445,26 @@
 }
 $0",
     );
-    {
-        let events = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
-        assert!(format!("{events:?}").contains("trait_impls_in_crate_shim"))
-    }
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "trait_impls_in_crate_shim",
+            ]
+        "#]],
+    );
 
     let new_text = "
 fn foo() -> i32 {
@@ -332,30 +488,243 @@
 
     db.set_file_text(pos.file_id.file_id(&db), new_text);
 
-    {
-        let actual = db.log_executed(|| {
+    execute_assert_events(
+        &db,
+        || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
             db.trait_impls_in_crate(module.krate());
-        });
+        },
+        &[("trait_impls_in_crate_shim", 1)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "crate_local_def_map",
+                "trait_impls_in_crate_shim",
+                "attrs_shim",
+                "impl_trait_with_diagnostics_shim",
+                "impl_signature_shim",
+                "impl_signature_with_source_map_shim",
+                "impl_self_ty_with_diagnostics_shim",
+                "struct_signature_shim",
+                "struct_signature_with_source_map_shim",
+                "attrs_shim",
+                "type_for_adt_tracked",
+            ]
+        "#]],
+    );
+}
 
-        let expected = vec![
-            "parse_shim".to_owned(),
-            "ast_id_map_shim".to_owned(),
-            "file_item_tree_shim".to_owned(),
-            "real_span_map_shim".to_owned(),
-            "crate_local_def_map".to_owned(),
-            "trait_impls_in_crate_shim".to_owned(),
-            "attrs_shim".to_owned(),
-            "impl_trait_with_diagnostics_shim".to_owned(),
-            "impl_signature_shim".to_owned(),
-            "impl_signature_with_source_map_shim".to_owned(),
-            "impl_self_ty_with_diagnostics_shim".to_owned(),
-            "struct_signature_shim".to_owned(),
-            "struct_signature_with_source_map_shim".to_owned(),
-            "type_for_adt_tracked".to_owned(),
-        ];
+#[test]
+fn add_struct_invalidates_trait_solve() {
+    let (mut db, file_id) = TestDB::with_single_file(
+        "
+//- /main.rs crate:main
+struct SomeStruct;
 
-        assert_eq!(expected, actual);
+trait Trait<T> {
+    fn method(&self) -> T;
+}
+impl Trait<u32> for SomeStruct {}
+
+fn main() {
+    let s = SomeStruct;
+    s.method();
+    s.$0
+}",
+    );
+
+    execute_assert_events(
+        &db,
+        || {
+            let module = db.module_for_file(file_id.file_id(&db));
+            let crate_def_map = module.def_map(&db);
+            let mut defs: Vec<DefWithBodyId> = vec![];
+            visit_module(&db, crate_def_map, module.local_id, &mut |it| {
+                let def = match it {
+                    ModuleDefId::FunctionId(it) => it.into(),
+                    ModuleDefId::EnumVariantId(it) => it.into(),
+                    ModuleDefId::ConstId(it) => it.into(),
+                    ModuleDefId::StaticId(it) => it.into(),
+                    _ => return,
+                };
+                defs.push(def);
+            });
+
+            for def in defs {
+                let _inference_result = db.infer(def);
+            }
+        },
+        &[("trait_solve_shim", 2)],
+        expect_test::expect![[r#"
+            [
+                "source_root_crates_shim",
+                "crate_local_def_map",
+                "file_item_tree_query",
+                "ast_id_map_shim",
+                "parse_shim",
+                "real_span_map_shim",
+                "trait_items_with_diagnostics_shim",
+                "body_shim",
+                "body_with_source_map_shim",
+                "attrs_shim",
+                "of_",
+                "infer_shim",
+                "trait_signature_shim",
+                "trait_signature_with_source_map_shim",
+                "attrs_shim",
+                "function_signature_shim",
+                "function_signature_with_source_map_shim",
+                "attrs_shim",
+                "body_shim",
+                "body_with_source_map_shim",
+                "trait_environment_shim",
+                "lang_item",
+                "crate_lang_items",
+                "attrs_shim",
+                "attrs_shim",
+                "return_type_impl_traits_shim",
+                "infer_shim",
+                "function_signature_shim",
+                "function_signature_with_source_map_shim",
+                "trait_environment_shim",
+                "expr_scopes_shim",
+                "struct_signature_shim",
+                "struct_signature_with_source_map_shim",
+                "generic_predicates_shim",
+                "value_ty_shim",
+                "variant_fields_shim",
+                "variant_fields_with_source_map_shim",
+                "lang_item",
+                "inherent_impls_in_crate_shim",
+                "impl_signature_shim",
+                "impl_signature_with_source_map_shim",
+                "callable_item_signature_shim",
+                "adt_variance_shim",
+                "variances_of_shim",
+                "trait_solve_shim",
+                "trait_datum_shim",
+                "generic_predicates_shim",
+                "adt_datum_shim",
+                "trait_impls_in_deps_shim",
+                "trait_impls_in_crate_shim",
+                "impl_trait_with_diagnostics_shim",
+                "impl_self_ty_with_diagnostics_shim",
+                "type_for_adt_tracked",
+                "impl_datum_shim",
+                "generic_predicates_shim",
+                "program_clauses_for_chalk_env_shim",
+                "value_ty_shim",
+                "generic_predicates_shim",
+                "trait_solve_shim",
+                "lang_item",
+            ]
+        "#]],
+    );
+
+    let new_text = "
+//- /main.rs crate:main
+struct AnotherStruct;
+
+struct SomeStruct;
+
+trait Trait<T> {
+    fn method(&self) -> T;
+}
+impl Trait<u32> for SomeStruct {}
+
+fn main() {
+    let s = SomeStruct;
+    s.method();
+    s.$0
+}";
+
+    db.set_file_text(file_id.file_id(&db), new_text);
+
+    execute_assert_events(
+        &db,
+        || {
+            let module = db.module_for_file(file_id.file_id(&db));
+            let crate_def_map = module.def_map(&db);
+            let mut defs: Vec<DefWithBodyId> = vec![];
+
+            visit_module(&db, crate_def_map, module.local_id, &mut |it| {
+                let def = match it {
+                    ModuleDefId::FunctionId(it) => it.into(),
+                    ModuleDefId::EnumVariantId(it) => it.into(),
+                    ModuleDefId::ConstId(it) => it.into(),
+                    ModuleDefId::StaticId(it) => it.into(),
+                    _ => return,
+                };
+                defs.push(def);
+            });
+
+            for def in defs {
+                let _inference_result = db.infer(def);
+            }
+        },
+        &[("trait_solve_shim", 0)],
+        expect_test::expect![[r#"
+            [
+                "parse_shim",
+                "ast_id_map_shim",
+                "file_item_tree_query",
+                "real_span_map_shim",
+                "crate_local_def_map",
+                "trait_items_with_diagnostics_shim",
+                "body_with_source_map_shim",
+                "attrs_shim",
+                "body_shim",
+                "of_",
+                "infer_shim",
+                "attrs_shim",
+                "trait_signature_with_source_map_shim",
+                "attrs_shim",
+                "function_signature_with_source_map_shim",
+                "function_signature_shim",
+                "body_with_source_map_shim",
+                "body_shim",
+                "trait_environment_shim",
+                "crate_lang_items",
+                "attrs_shim",
+                "attrs_shim",
+                "attrs_shim",
+                "return_type_impl_traits_shim",
+                "infer_shim",
+                "function_signature_with_source_map_shim",
+                "trait_environment_shim",
+                "expr_scopes_shim",
+                "struct_signature_with_source_map_shim",
+                "generic_predicates_shim",
+                "variant_fields_with_source_map_shim",
+                "inherent_impls_in_crate_shim",
+                "impl_signature_with_source_map_shim",
+                "impl_signature_shim",
+                "callable_item_signature_shim",
+                "generic_predicates_shim",
+                "trait_impls_in_crate_shim",
+                "impl_trait_with_diagnostics_shim",
+                "impl_self_ty_with_diagnostics_shim",
+                "generic_predicates_shim",
+                "generic_predicates_shim",
+            ]
+        "#]],
+    );
+}
+
+fn execute_assert_events(
+    db: &TestDB,
+    f: impl FnOnce(),
+    required: &[(&str, usize)],
+    expect: Expect,
+) {
+    let events = db.log_executed(f);
+    for (event, count) in required {
+        let n = events.iter().filter(|it| it.contains(event)).count();
+        assert_eq!(n, *count, "Expected {event} to be executed {count} times, but only got {n}");
     }
+    expect.assert_debug_eq(&events);
 }
diff --git a/crates/hir-ty/src/tls.rs b/crates/hir-ty/src/tls.rs
index f5911e2..f53409a 100644
--- a/crates/hir-ty/src/tls.rs
+++ b/crates/hir-ty/src/tls.rs
@@ -109,7 +109,7 @@
             CallableDefId::StructId(s) => self.0.struct_signature(s).name.clone(),
             CallableDefId::EnumVariantId(e) => {
                 let loc = e.lookup(self.0);
-                self.0.enum_variants(loc.parent).variants[loc.index as usize].1.clone()
+                loc.parent.enum_variants(self.0).variants[loc.index as usize].1.clone()
             }
         };
         match def {
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 1e0ff42..4c8e635 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -293,9 +293,7 @@
     let loc = func.lookup(db);
     match loc.container {
         hir_def::ItemContainerId::ExternBlockId(block) => {
-            let id = block.lookup(db).id;
-            let is_intrinsic_block =
-                id.item_tree(db)[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic);
+            let is_intrinsic_block = block.abi(db) == Some(sym::rust_dash_intrinsic);
             if is_intrinsic_block {
                 // legacy intrinsics
                 // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
@@ -357,7 +355,7 @@
     let (var_id, var_layout) = match &layout.variants {
         hir_def::layout::Variants::Empty => unreachable!(),
         hir_def::layout::Variants::Single { index } => {
-            (db.enum_variants(e).variants[index.0].0, layout)
+            (e.enum_variants(db).variants[index.0].0, layout)
         }
         hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
             let size = tag.size(target_data_layout).bytes_usize();
@@ -367,7 +365,7 @@
                 TagEncoding::Direct => {
                     let (var_idx, layout) =
                         variants.iter_enumerated().find_map(|(var_idx, v)| {
-                            let def = db.enum_variants(e).variants[var_idx.0].0;
+                            let def = e.enum_variants(db).variants[var_idx.0].0;
                             (db.const_eval_discriminant(def) == Ok(tag)).then_some((def, v))
                         })?;
                     (var_idx, layout)
@@ -380,7 +378,7 @@
                         .filter(|x| x != untagged_variant)
                         .nth(candidate_tag)
                         .unwrap_or(*untagged_variant);
-                    (db.enum_variants(e).variants[variant.0].0, &variants[variant])
+                    (e.enum_variants(db).variants[variant.0].0, &variants[variant])
                 }
             }
         }
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index d6b43ae..08a215f 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -213,7 +213,7 @@
                     AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)),
                     AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)),
                     AdtId::EnumId(e) => {
-                        db.enum_variants(e).variants.iter().for_each(|&(variant, _)| {
+                        e.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| {
                             add_constraints_from_variant(VariantId::EnumVariantId(variant))
                         });
                     }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e8218cf..adae335 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -52,12 +52,14 @@
         BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat,
         generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
     },
-    item_tree::{AttrOwner, FieldParent, ImportAlias, ItemTreeFieldId, ItemTreeNode},
+    item_tree::ImportAlias,
     layout::{self, ReprOptions, TargetDataLayout},
     nameres::{self, diagnostics::DefDiagnostic},
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields},
+    src::HasSource as _,
+    visibility::visibility_from_ast,
 };
 use hir_expand::{
     AstId, MacroCallKind, RenderedExpandError, ValueResult, attrs::collect_attrs,
@@ -81,11 +83,11 @@
 use nameres::diagnostics::DefDiagnosticKind;
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
-use span::{Edition, FileId};
+use span::{AstIdNode, Edition, FileId};
 use stdx::{format_to, impl_from, never};
 use syntax::{
     AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, T, TextRange, ToSmolStr,
-    ast::{self, HasAttrs as _, HasName},
+    ast::{self, HasAttrs as _, HasName, HasVisibility as _},
     format_smolstr,
 };
 use triomphe::{Arc, ThinArc};
@@ -686,8 +688,8 @@
                         Adt::Enum(e) => {
                             let source_map = db.enum_signature_with_source_map(e.id).1;
                             expr_store_diagnostics(db, acc, &source_map);
-                            let (variants, diagnostics) = db.enum_variants_with_diagnostics(e.id);
-                            let file = e.id.lookup(db).id.file_id();
+                            let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db);
+                            let file = e.id.lookup(db).id.file_id;
                             let ast_id_map = db.ast_id_map(file);
                             if let Some(diagnostics) = &diagnostics {
                                 for diag in diagnostics.iter() {
@@ -704,7 +706,7 @@
                                     );
                                 }
                             }
-                            for &(v, _) in &variants.variants {
+                            for &(v, _, _) in &variants.variants {
                                 let source_map = db.variant_fields_with_source_map(v.into()).1;
                                 push_ty_diagnostics(
                                     db,
@@ -742,12 +744,10 @@
             GenericDef::Impl(impl_def).diagnostics(db, acc);
 
             let loc = impl_def.id.lookup(db);
-            let tree = loc.id.item_tree(db);
             let source_map = db.impl_signature_with_source_map(impl_def.id).1;
             expr_store_diagnostics(db, acc, &source_map);
 
-            let node = &tree[loc.id.value];
-            let file_id = loc.id.file_id();
+            let file_id = loc.id.file_id;
             if file_id.macro_file().is_some_and(|it| it.kind(db) == MacroKind::DeriveBuiltIn) {
                 // these expansion come from us, diagnosing them is a waste of resources
                 // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow
@@ -760,16 +760,16 @@
 
             let ast_id_map = db.ast_id_map(file_id);
 
-            for diag in db.impl_items_with_diagnostics(impl_def.id).1.iter() {
+            for diag in impl_def.id.impl_items_with_diagnostics(db).1.iter() {
                 emit_def_diagnostic(db, acc, diag, edition);
             }
 
             if inherent_impls.invalid_impls().contains(&impl_def.id) {
-                acc.push(IncoherentImpl { impl_: ast_id_map.get(node.ast_id()), file_id }.into())
+                acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into())
             }
 
             if !impl_def.check_orphan_rules(db) {
-                acc.push(TraitImplOrphan { impl_: ast_id_map.get(node.ast_id()), file_id }.into())
+                acc.push(TraitImplOrphan { impl_: ast_id_map.get(loc.id.value), file_id }.into())
             }
 
             let trait_ = impl_def.trait_(db);
@@ -808,11 +808,11 @@
                 // unsafe negative impl
                 (true, _, true, _) |
                 // unsafe impl for safe trait
-                (true, false, _, false) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: true }.into()),
+                (true, false, _, false) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(loc.id.value), file_id, should_be_safe: true }.into()),
                 // safe impl for unsafe trait
                 (false, true, false, _) |
                 // safe impl of dangling drop
-                (false, false, _, true) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(node.ast_id()), file_id, should_be_safe: false }.into()),
+                (false, false, _, true) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(loc.id.value), file_id, should_be_safe: false }.into()),
                 _ => (),
             };
 
@@ -824,7 +824,7 @@
                     AssocItemId::ConstId(id) => !db.const_signature(id).has_body(),
                     AssocItemId::TypeAliasId(it) => db.type_alias_signature(it).ty.is_none(),
                 });
-                impl_assoc_items_scratch.extend(db.impl_items(impl_def.id).items.iter().cloned());
+                impl_assoc_items_scratch.extend(impl_def.id.impl_items(db).items.iter().cloned());
 
                 let redundant = impl_assoc_items_scratch
                     .iter()
@@ -839,14 +839,14 @@
                         TraitImplRedundantAssocItems {
                             trait_,
                             file_id,
-                            impl_: ast_id_map.get(node.ast_id()),
+                            impl_: ast_id_map.get(loc.id.value),
                             assoc_item: (name, assoc_item),
                         }
                         .into(),
                     )
                 }
 
-                let missing: Vec<_> = required_items
+                let mut missing: Vec<_> = required_items
                     .filter(|(name, id)| {
                         !impl_assoc_items_scratch.iter().any(|(impl_name, impl_item)| {
                             discriminant(impl_item) == discriminant(id) && impl_name == name
@@ -854,10 +854,42 @@
                     })
                     .map(|(name, item)| (name.clone(), AssocItem::from(*item)))
                     .collect();
+
+                if !missing.is_empty() {
+                    let self_ty = db.impl_self_ty(impl_def.id).substitute(
+                        Interner,
+                        &hir_ty::generics::generics(db, impl_def.id.into()).placeholder_subst(db),
+                    );
+                    let self_ty = if let TyKind::Alias(AliasTy::Projection(projection)) =
+                        self_ty.kind(Interner)
+                    {
+                        db.normalize_projection(
+                            projection.clone(),
+                            db.trait_environment(impl_def.id.into()),
+                        )
+                    } else {
+                        self_ty
+                    };
+                    let self_ty_is_guaranteed_unsized = matches!(
+                        self_ty.kind(Interner),
+                        TyKind::Dyn(..) | TyKind::Slice(..) | TyKind::Str
+                    );
+                    if self_ty_is_guaranteed_unsized {
+                        missing.retain(|(_, assoc_item)| {
+                            let assoc_item = match *assoc_item {
+                                AssocItem::Function(it) => it.id.into(),
+                                AssocItem::Const(it) => it.id.into(),
+                                AssocItem::TypeAlias(it) => it.id.into(),
+                            };
+                            !hir_ty::dyn_compatibility::generics_require_sized_self(db, assoc_item)
+                        });
+                    }
+                }
+
                 if !missing.is_empty() {
                     acc.push(
                         TraitImplMissingAssocItems {
-                            impl_: ast_id_map.get(node.ast_id()),
+                            impl_: ast_id_map.get(loc.id.value),
                             file_id,
                             missing,
                         }
@@ -880,7 +912,7 @@
                 &source_map,
             );
 
-            for &(_, item) in db.impl_items(impl_def.id).items.iter() {
+            for &(_, item) in impl_def.id.impl_items(db).items.iter() {
                 AssocItem::from(item).diagnostics(db, acc, style_lints);
             }
         }
@@ -1044,73 +1076,25 @@
             )
         }
         DefDiagnosticKind::UnresolvedImport { id, index } => {
-            let file_id = id.file_id();
-            let item_tree = id.item_tree(db);
-            let import = &item_tree[id.value];
+            let file_id = id.file_id;
 
-            let use_tree = import.use_tree_to_ast(db, file_id, *index);
+            let use_tree = hir_def::src::use_tree_to_ast(db, *id, *index);
             acc.push(
                 UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }.into(),
             );
         }
 
-        DefDiagnosticKind::UnconfiguredCode { tree, item, cfg, opts } => {
-            let item_tree = tree.item_tree(db);
-            let ast_id_map = db.ast_id_map(tree.file_id());
-            // FIXME: This parses... We could probably store relative ranges for the children things
-            // here in the item tree?
-            (|| {
-                let process_field_list =
-                    |field_list: Option<_>, idx: ItemTreeFieldId| match field_list? {
-                        ast::FieldList::RecordFieldList(it) => Some(SyntaxNodePtr::new(
-                            it.fields().nth(idx.into_raw().into_u32() as usize)?.syntax(),
-                        )),
-                        ast::FieldList::TupleFieldList(it) => Some(SyntaxNodePtr::new(
-                            it.fields().nth(idx.into_raw().into_u32() as usize)?.syntax(),
-                        )),
-                    };
-                let ptr = match *item {
-                    AttrOwner::ModItem(it) => {
-                        ast_id_map.get(it.ast_id(&item_tree)).syntax_node_ptr()
-                    }
-                    AttrOwner::TopLevel => ast_id_map.root(),
-                    AttrOwner::Variant(it) => {
-                        ast_id_map.get(item_tree[it].ast_id).syntax_node_ptr()
-                    }
-                    AttrOwner::Field(FieldParent::EnumVariant(parent), idx) => process_field_list(
-                        ast_id_map
-                            .get(item_tree[parent].ast_id)
-                            .to_node(&db.parse_or_expand(tree.file_id()))
-                            .field_list(),
-                        idx,
-                    )?,
-                    AttrOwner::Field(FieldParent::Struct(parent), idx) => process_field_list(
-                        ast_id_map
-                            .get(item_tree[parent.index()].ast_id)
-                            .to_node(&db.parse_or_expand(tree.file_id()))
-                            .field_list(),
-                        idx,
-                    )?,
-                    AttrOwner::Field(FieldParent::Union(parent), idx) => SyntaxNodePtr::new(
-                        ast_id_map
-                            .get(item_tree[parent.index()].ast_id)
-                            .to_node(&db.parse_or_expand(tree.file_id()))
-                            .record_field_list()?
-                            .fields()
-                            .nth(idx.into_raw().into_u32() as usize)?
-                            .syntax(),
-                    ),
-                };
-                acc.push(
-                    InactiveCode {
-                        node: InFile::new(tree.file_id(), ptr),
-                        cfg: cfg.clone(),
-                        opts: opts.clone(),
-                    }
-                    .into(),
-                );
-                Some(())
-            })();
+        DefDiagnosticKind::UnconfiguredCode { ast_id, cfg, opts } => {
+            let ast_id_map = db.ast_id_map(ast_id.file_id);
+            let ptr = ast_id_map.get_erased(ast_id.value);
+            acc.push(
+                InactiveCode {
+                    node: InFile::new(ast_id.file_id, ptr),
+                    cfg: cfg.clone(),
+                    opts: opts.clone(),
+                }
+                .into(),
+            );
         }
         DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
             let (node, precise_location) = precise_macro_call_location(ast, db);
@@ -1446,12 +1430,8 @@
 impl HasVisibility for Struct {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -1504,12 +1484,8 @@
 impl HasVisibility for Union {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -1528,11 +1504,11 @@
     }
 
     pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
-        db.enum_variants(self.id).variants.iter().map(|&(id, _)| Variant { id }).collect()
+        self.id.enum_variants(db).variants.iter().map(|&(id, _, _)| Variant { id }).collect()
     }
 
     pub fn num_variants(self, db: &dyn HirDatabase) -> usize {
-        db.enum_variants(self.id).variants.len()
+        self.id.enum_variants(db).variants.len()
     }
 
     pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
@@ -1597,12 +1573,8 @@
 impl HasVisibility for Enum {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -1634,7 +1606,7 @@
     pub fn name(self, db: &dyn HirDatabase) -> Name {
         let lookup = self.id.lookup(db);
         let enum_ = lookup.parent;
-        db.enum_variants(enum_).variants[lookup.index as usize].1.clone()
+        enum_.enum_variants(db).variants[lookup.index as usize].1.clone()
     }
 
     pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
@@ -2660,7 +2632,7 @@
 
 impl HasVisibility for Function {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        db.function_visibility(self.id)
+        db.assoc_visibility(self.id.into())
     }
 }
 
@@ -2676,10 +2648,9 @@
 
     pub fn resolved_crate(self, db: &dyn HirDatabase) -> Option<Crate> {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
         let krate = loc.container.krate();
-        let name = &item_tree[loc.id.value].name;
-        if *name == sym::self_ {
+        let name = self.name(db);
+        if name == sym::self_ {
             Some(krate.into())
         } else {
             krate.data(db).dependencies.iter().find_map(|dep| {
@@ -2690,25 +2661,29 @@
 
     pub fn name(self, db: &dyn HirDatabase) -> Name {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        item_tree[loc.id.value].name.clone()
+        let source = loc.source(db);
+        as_name_opt(source.value.name_ref())
     }
 
     pub fn alias(self, db: &dyn HirDatabase) -> Option<ImportAlias> {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        item_tree[loc.id.value].alias.clone()
+        let source = loc.source(db);
+        let rename = source.value.rename()?;
+        if let Some(name) = rename.name() {
+            Some(ImportAlias::Alias(name.as_name()))
+        } else if rename.underscore_token().is_some() {
+            Some(ImportAlias::Underscore)
+        } else {
+            None
+        }
     }
 
     /// Returns the name under which this crate is made accessible, taking `_` into account.
     pub fn alias_or_name(self, db: &dyn HirDatabase) -> Option<Name> {
-        let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-
-        match &item_tree[loc.id.value].alias {
+        match self.alias(db) {
             Some(ImportAlias::Underscore) => None,
-            Some(ImportAlias::Alias(alias)) => Some(alias.clone()),
-            None => Some(item_tree[loc.id.value].name.clone()),
+            Some(ImportAlias::Alias(alias)) => Some(alias),
+            None => Some(self.name(db)),
         }
     }
 }
@@ -2716,12 +2691,8 @@
 impl HasVisibility for ExternCrateDecl {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -2756,7 +2727,7 @@
 
 impl HasVisibility for Const {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        db.const_visibility(self.id)
+        db.assoc_visibility(self.id.into())
     }
 }
 
@@ -2841,12 +2812,8 @@
 impl HasVisibility for Static {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -2935,11 +2902,7 @@
     }
 
     fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
-        db.trait_items(self.id)
-            .macro_calls
-            .as_ref()
-            .map(|it| it.as_ref().clone().into_boxed_slice())
-            .unwrap_or_default()
+        db.trait_items(self.id).macro_calls.to_vec().into_boxed_slice()
     }
 
     /// `#[rust_analyzer::completions(...)]` mode.
@@ -2951,12 +2914,8 @@
 impl HasVisibility for Trait {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -2978,12 +2937,8 @@
 impl HasVisibility for TraitAlias {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
         let loc = self.id.lookup(db);
-        let item_tree = loc.id.item_tree(db);
-        Visibility::resolve(
-            db,
-            &self.id.resolver(db),
-            &item_tree[item_tree[loc.id.value].visibility],
-        )
+        let source = loc.source(db);
+        visibility_from_ast(db, self.id, source.map(|src| src.visibility()))
     }
 }
 
@@ -3021,7 +2976,7 @@
 
 impl HasVisibility for TypeAlias {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        db.type_alias_visibility(self.id)
+        db.assoc_visibility(self.id.into())
     }
 }
 
@@ -3131,25 +3086,23 @@
         match self.id {
             MacroId::Macro2Id(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
-                item_tree[loc.id.value].name.clone()
+                let source = loc.source(db);
+                as_name_opt(source.value.name())
             }
             MacroId::MacroRulesId(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
-                item_tree[loc.id.value].name.clone()
+                let source = loc.source(db);
+                as_name_opt(source.value.name())
             }
             MacroId::ProcMacroId(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
+                let source = loc.source(db);
                 match loc.kind {
                     ProcMacroKind::CustomDerive => db
                         .attrs(id.into())
                         .parse_proc_macro_derive()
-                        .map_or_else(|| item_tree[loc.id.value].name.clone(), |(it, _)| it),
-                    ProcMacroKind::Bang | ProcMacroKind::Attr => {
-                        item_tree[loc.id.value].name.clone()
-                    }
+                        .map_or_else(|| as_name_opt(source.value.name()), |(it, _)| it),
+                    ProcMacroKind::Bang | ProcMacroKind::Attr => as_name_opt(source.value.name()),
                 }
             }
         }
@@ -3246,12 +3199,8 @@
         match self.id {
             MacroId::Macro2Id(id) => {
                 let loc = id.lookup(db);
-                let item_tree = loc.id.item_tree(db);
-                Visibility::resolve(
-                    db,
-                    &id.resolver(db),
-                    &item_tree[item_tree[loc.id.value].visibility],
-                )
+                let source = loc.source(db);
+                visibility_from_ast(db, id, source.map(|src| src.visibility()))
             }
             MacroId::MacroRulesId(_) => Visibility::Public,
             MacroId::ProcMacroId(_) => Visibility::Public,
@@ -3405,7 +3354,7 @@
 where
     ID: Lookup<Database = dyn DefDatabase, Data = AssocItemLoc<LOC>>,
     DEF: From<ID>,
-    LOC: ItemTreeNode,
+    LOC: AstIdNode,
 {
     match id.lookup(db).container {
         ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
@@ -3421,7 +3370,7 @@
 where
     ID: Lookup<Database = dyn DefDatabase, Data = AssocItemLoc<LOC>>,
     DEF: From<ID>,
-    LOC: ItemTreeNode,
+    LOC: AstIdNode,
 {
     match id.lookup(db).container {
         ItemContainerId::ExternBlockId(_) => Some(ctor(DEF::from(id))),
@@ -4464,7 +4413,7 @@
     }
 
     pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
-        db.impl_items(self.id).items.iter().map(|&(_, it)| it.into()).collect()
+        self.id.impl_items(db).items.iter().map(|&(_, it)| it.into()).collect()
     }
 
     pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
@@ -4513,11 +4462,7 @@
     }
 
     fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
-        db.impl_items(self.id)
-            .macro_calls
-            .as_ref()
-            .map(|it| it.as_ref().clone().into_boxed_slice())
-            .unwrap_or_default()
+        self.id.impl_items(db).macro_calls.to_vec().into_boxed_slice()
     }
 }
 
@@ -5326,7 +5271,7 @@
             let impls = db.inherent_impls_in_crate(krate);
 
             for impl_def in impls.for_self_ty(&self.ty) {
-                for &(_, item) in db.impl_items(*impl_def).items.iter() {
+                for &(_, item) in impl_def.impl_items(db).items.iter() {
                     if callback(item) {
                         return;
                     }
@@ -6478,3 +6423,7 @@
         })
         .flatten()
 }
+
+fn as_name_opt(name: Option<impl AsName>) -> Name {
+    name.map_or_else(Name::missing, |name| name.as_name())
+}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 4a2e8e3..1049895 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -25,7 +25,6 @@
     builtin::{BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
     files::{FileRangeWrapper, HirFileRange, InRealFile},
-    inert_attr_macro::find_builtin_attr_idx,
     mod_path::{ModPath, PathKind},
     name::AsName,
 };
@@ -159,13 +158,13 @@
     macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroCallId>>,
 }
 
-impl<DB> fmt::Debug for Semantics<'_, DB> {
+impl<DB: ?Sized> fmt::Debug for Semantics<'_, DB> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "Semantics {{ ... }}")
     }
 }
 
-impl<'db, DB> ops::Deref for Semantics<'db, DB> {
+impl<'db, DB: ?Sized> ops::Deref for Semantics<'db, DB> {
     type Target = SemanticsImpl<'db>;
 
     fn deref(&self) -> &Self::Target {
@@ -173,12 +172,28 @@
     }
 }
 
+// Note: while this variant of `Semantics<'_, _>` might seem unused, as it does not
+// find actual use within the rust-analyzer project itself, it exists to enable the use
+// within e.g. tracked salsa functions in third-party crates that build upon `ra_ap_hir`.
+impl Semantics<'_, dyn HirDatabase> {
+    /// Creates an instance that's weakly coupled to its underlying database type.
+    pub fn new_dyn(db: &'_ dyn HirDatabase) -> Semantics<'_, dyn HirDatabase> {
+        let impl_ = SemanticsImpl::new(db);
+        Semantics { db, imp: impl_ }
+    }
+}
+
 impl<DB: HirDatabase> Semantics<'_, DB> {
+    /// Creates an instance that's strongly coupled to its underlying database type.
     pub fn new(db: &DB) -> Semantics<'_, DB> {
         let impl_ = SemanticsImpl::new(db);
         Semantics { db, imp: impl_ }
     }
+}
 
+// Note: We take `DB` as `?Sized` here in order to support type-erased
+// use of `Semantics` via `Semantics<'_, dyn HirDatabase>`:
+impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
     pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId {
         self.imp.find_file(syntax_node).file_id
     }
@@ -229,7 +244,7 @@
         offset: TextSize,
     ) -> impl Iterator<Item = ast::NameLike> + 'slf {
         node.token_at_offset(offset)
-            .map(move |token| self.descend_into_macros_no_opaque(token))
+            .map(move |token| self.descend_into_macros_no_opaque(token, true))
             .map(|descendants| descendants.into_iter().filter_map(move |it| it.value.parent()))
             // re-order the tokens from token_at_offset by returning the ancestors with the smaller first nodes first
             // See algo::ancestors_at_offset, which uses the same approach
@@ -953,13 +968,6 @@
             let Some(item) = ast::Item::cast(ancestor) else {
                 return false;
             };
-            // Optimization to skip the semantic check.
-            if item.attrs().all(|attr| {
-                attr.simple_name()
-                    .is_some_and(|attr| find_builtin_attr_idx(&Symbol::intern(&attr)).is_some())
-            }) {
-                return false;
-            }
             self.with_ctx(|ctx| {
                 if ctx.item_to_macro_call(token.with_value(&item)).is_some() {
                     return true;
@@ -1001,10 +1009,11 @@
     pub fn descend_into_macros_no_opaque(
         &self,
         token: SyntaxToken,
+        always_descend_into_derives: bool,
     ) -> SmallVec<[InFile<SyntaxToken>; 1]> {
         let mut res = smallvec![];
         let token = self.wrap_token_infile(token);
-        self.descend_into_macros_all(token.clone(), true, &mut |t, ctx| {
+        self.descend_into_macros_all(token.clone(), always_descend_into_derives, &mut |t, ctx| {
             if !ctx.is_opaque(self.db) {
                 // Don't descend into opaque contexts
                 res.push(t);
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index 1a6d63c..fedd823 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -6,10 +6,11 @@
 
 use either::Either;
 use hir_expand::{HirFileId, attrs::collect_attrs};
+use span::AstIdNode;
 use syntax::{AstPtr, ast};
 
 use hir_def::{
-    AdtId, AssocItemId, DefWithBodyId, EnumId, FieldId, GenericDefId, ImplId, ItemTreeLoc,
+    AdtId, AssocItemId, AstIdLoc, DefWithBodyId, EnumId, FieldId, GenericDefId, ImplId,
     LifetimeParamId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, TypeOrConstParamId,
     VariantId,
     db::DefDatabase,
@@ -19,7 +20,6 @@
     },
     hir::generics::GenericParams,
     item_scope::ItemScope,
-    item_tree::ItemTreeNode,
     nameres::DefMap,
     src::{HasChildSource, HasSource},
 };
@@ -61,7 +61,7 @@
 
 impl ChildBySource for ImplId {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
-        let data = db.impl_items(*self);
+        let data = self.impl_items(db);
         data.macro_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
             |(ast_id, call_id)| {
                 let ptr = ast_id.to_ptr(db);
@@ -113,7 +113,7 @@
             ids.iter().for_each(|&id| {
                 if let MacroId::MacroRulesId(id) = id {
                     let loc = id.lookup(db);
-                    if loc.id.file_id() == file_id {
+                    if loc.id.file_id == file_id {
                         res[keys::MACRO_RULES].insert(loc.ast_ptr(db).value, id);
                     }
                 }
@@ -199,16 +199,14 @@
 impl ChildBySource for EnumId {
     fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
         let loc = &self.lookup(db);
-        if file_id != loc.id.file_id() {
+        if file_id != loc.id.file_id {
             return;
         }
 
-        let tree = loc.id.item_tree(db);
-        let ast_id_map = db.ast_id_map(loc.id.file_id());
+        let ast_id_map = db.ast_id_map(loc.id.file_id);
 
-        db.enum_variants(*self).variants.iter().for_each(|&(variant, _)| {
-            res[keys::ENUM_VARIANT]
-                .insert(ast_id_map.get(tree[variant.lookup(db).id.value].ast_id), variant);
+        self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| {
+            res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant);
         });
         let (_, source_map) = db.enum_signature_with_source_map(*self);
         source_map
@@ -287,15 +285,14 @@
     res: &mut DynMap,
     file_id: HirFileId,
     id: ID,
-    key: Key<N::Source, ID>,
+    key: Key<N, ID>,
 ) where
     ID: Lookup<Database = dyn DefDatabase, Data = Data> + 'static,
-    Data: ItemTreeLoc<Id = N>,
-    N: ItemTreeNode,
-    N::Source: 'static,
+    Data: AstIdLoc<Ast = N>,
+    N: AstIdNode + 'static,
 {
     let loc = id.lookup(db);
-    if loc.item_tree_id().file_id() == file_id {
+    if loc.ast_id().file_id == file_id {
         res[key].insert(loc.ast_ptr(db).value, id)
     }
 }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index ec2ccf8..3273358 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -37,7 +37,7 @@
 };
 use hir_ty::{
     Adjustment, AliasTy, InferenceResult, Interner, LifetimeElisionKind, ProjectionTy,
-    Substitution, TraitEnvironment, Ty, TyExt, TyKind, TyLoweringContext,
+    Substitution, ToChalk, TraitEnvironment, Ty, TyExt, TyKind, TyLoweringContext,
     diagnostics::{
         InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields,
         unsafe_operations,
@@ -829,7 +829,7 @@
                                 handle_variants(id.into(), subst, &mut container)?
                             }
                             AdtId::EnumId(id) => {
-                                let variants = db.enum_variants(id);
+                                let variants = id.enum_variants(db);
                                 let variant = variants.variant(&field_name.as_name())?;
                                 container = Either::Left((variant, subst.clone()));
                                 (Either::Left(Variant { id: variant }), id.into(), subst.clone())
@@ -1169,8 +1169,7 @@
                         )
                     }
                     TyKind::FnDef(fn_id, subst) => {
-                        let fn_id = hir_ty::db::InternedCallableDefId::from(*fn_id);
-                        let fn_id = db.lookup_intern_callable_def(fn_id);
+                        let fn_id = ToChalk::from_chalk(db, *fn_id);
                         let generic_def_id = match fn_id {
                             CallableDefId::StructId(id) => id.into(),
                             CallableDefId::FunctionId(id) => id.into(),
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index e87ab87..64f2a91 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -1,5 +1,6 @@
 //! File symbol extraction.
 
+use base_db::FxIndexSet;
 use either::Either;
 use hir_def::{
     AdtId, AssocItemId, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId,
@@ -21,8 +22,6 @@
 
 use crate::{HasCrate, Module, ModuleDef, Semantics};
 
-pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
-
 /// The actual data that is stored in the index. It should be as compact as
 /// possible.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -34,6 +33,7 @@
     /// Whether this symbol is a doc alias for the original symbol.
     pub is_alias: bool,
     pub is_assoc: bool,
+    pub is_import: bool,
     pub do_not_complete: Complete,
 }
 
@@ -165,6 +165,7 @@
 
         let is_explicit_import = |vis| match vis {
             Visibility::Public => true,
+            Visibility::PubCrate(_) => true,
             Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
             Visibility::Module(_, VisibilityExplicitness::Implicit) => false,
         };
@@ -197,6 +198,7 @@
                 loc: dec_loc,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Complete::Yes,
             });
         };
@@ -227,6 +229,7 @@
                     loc: dec_loc,
                     is_alias: false,
                     is_assoc: false,
+                    is_import: false,
                     do_not_complete: Complete::Yes,
                 });
             };
@@ -322,7 +325,7 @@
                 .to_smolstr(),
         );
         self.with_container_name(impl_name, |s| {
-            for &(ref name, assoc_item_id) in &self.db.impl_items(impl_id).items {
+            for &(ref name, assoc_item_id) in &impl_id.impl_items(self.db).items {
                 s.push_assoc_item(assoc_item_id, name, None)
             }
         })
@@ -398,6 +401,7 @@
                     container_name: self.current_container_name.clone(),
                     is_alias: true,
                     is_assoc,
+                    is_import: false,
                     do_not_complete,
                 });
             }
@@ -410,6 +414,7 @@
             loc: dec_loc,
             is_alias: false,
             is_assoc,
+            is_import: false,
             do_not_complete,
         });
 
@@ -442,6 +447,7 @@
                     container_name: self.current_container_name.clone(),
                     is_alias: true,
                     is_assoc: false,
+                    is_import: false,
                     do_not_complete,
                 });
             }
@@ -454,6 +460,7 @@
             loc: dec_loc,
             is_alias: false,
             is_assoc: false,
+            is_import: false,
             do_not_complete,
         });
     }
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index 583318d..acde1d6 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -22,7 +22,7 @@
 itertools.workspace = true
 arrayvec.workspace = true
 indexmap.workspace = true
-memchr = "2.7.4"
+memchr = "2.7.5"
 salsa.workspace = true
 salsa-macros.workspace = true
 query-group.workspace = true
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index d1ba79e..c15cade 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -50,6 +50,7 @@
     case_sensitive: bool,
     only_types: bool,
     libs: bool,
+    exclude_imports: bool,
 }
 
 impl Query {
@@ -63,6 +64,7 @@
             mode: SearchMode::Fuzzy,
             assoc_mode: AssocSearchMode::Include,
             case_sensitive: false,
+            exclude_imports: false,
         }
     }
 
@@ -94,6 +96,10 @@
     pub fn case_sensitive(&mut self) {
         self.case_sensitive = true;
     }
+
+    pub fn exclude_imports(&mut self) {
+        self.exclude_imports = true;
+    }
 }
 
 #[query_group::query_group]
@@ -362,6 +368,9 @@
                     if ignore_underscore_prefixed && symbol_name.starts_with("__") {
                         continue;
                     }
+                    if self.exclude_imports && symbol.is_import {
+                        continue;
+                    }
                     if self.mode.check(&self.query, self.case_sensitive, symbol_name) {
                         if let Some(b) = cb(symbol).break_value() {
                             return Some(b);
@@ -385,7 +394,8 @@
 mod tests {
 
     use expect_test::expect_file;
-    use test_fixture::WithFixture;
+    use salsa::Durability;
+    use test_fixture::{WORKSPACE, WithFixture};
 
     use super::*;
 
@@ -506,4 +516,31 @@
 
         expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols);
     }
+
+    #[test]
+    fn test_exclude_imports() {
+        let (mut db, _) = RootDatabase::with_many_files(
+            r#"
+//- /lib.rs
+mod foo;
+pub use foo::Foo;
+
+//- /foo.rs
+pub struct Foo;
+"#,
+        );
+
+        let mut local_roots = FxHashSet::default();
+        local_roots.insert(WORKSPACE);
+        db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
+
+        let mut query = Query::new("Foo".to_owned());
+        let mut symbols = world_symbols(&db, query.clone());
+        symbols.sort_by_key(|x| x.is_import);
+        expect_file!["./test_data/test_symbols_with_imports.txt"].assert_debug_eq(&symbols);
+
+        query.exclude_imports();
+        let symbols = world_symbols(&db, query);
+        expect_file!["./test_data/test_symbols_exclude_imports.txt"].assert_debug_eq(&symbols);
+    }
 }
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt
index 455a680..30d1df4 100644
--- a/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -41,6 +41,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -74,6 +75,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -107,6 +109,7 @@
                 container_name: None,
                 is_alias: true,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -140,6 +143,7 @@
                 container_name: None,
                 is_alias: true,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -173,6 +177,7 @@
                 container_name: None,
                 is_alias: true,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -206,6 +211,7 @@
                 container_name: None,
                 is_alias: true,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -239,6 +245,7 @@
                 container_name: None,
                 is_alias: true,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
         ],
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 5e5ae1d..de046e7 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -39,6 +39,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -70,6 +71,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -101,6 +103,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -134,6 +137,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -167,6 +171,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -200,6 +205,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -231,6 +237,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -264,6 +271,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -297,6 +305,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -332,6 +341,7 @@
                 ),
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -367,6 +377,7 @@
                 ),
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -400,6 +411,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -433,6 +445,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -464,6 +477,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -497,6 +511,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -530,6 +545,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -565,6 +581,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -600,6 +617,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -633,6 +651,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -666,6 +685,7 @@
                 ),
                 is_alias: false,
                 is_assoc: true,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -699,6 +719,7 @@
                 ),
                 is_alias: false,
                 is_assoc: true,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -732,6 +753,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -763,6 +785,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -796,6 +819,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -829,6 +853,7 @@
                 ),
                 is_alias: false,
                 is_assoc: true,
+                is_import: false,
                 do_not_complete: Yes,
             },
         ],
@@ -875,6 +900,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
         ],
@@ -919,6 +945,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -952,6 +979,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -985,6 +1013,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: false,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -1018,6 +1047,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
             FileSymbol {
@@ -1051,6 +1081,7 @@
                 container_name: None,
                 is_alias: false,
                 is_assoc: false,
+                is_import: true,
                 do_not_complete: Yes,
             },
         ],
diff --git a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
new file mode 100644
index 0000000..22872b5
--- /dev/null
+++ b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
@@ -0,0 +1,36 @@
+[
+    FileSymbol {
+        name: "Foo",
+        def: Adt(
+            Struct(
+                Struct {
+                    id: StructId(
+                        3800,
+                    ),
+                },
+            ),
+        ),
+        loc: DeclarationLocation {
+            hir_file_id: FileId(
+                EditionedFileId(
+                    Id(2001),
+                ),
+            ),
+            ptr: SyntaxNodePtr {
+                kind: STRUCT,
+                range: 0..15,
+            },
+            name_ptr: AstPtr(
+                SyntaxNodePtr {
+                    kind: NAME,
+                    range: 11..14,
+                },
+            ),
+        },
+        container_name: None,
+        is_alias: false,
+        is_assoc: false,
+        is_import: false,
+        do_not_complete: Yes,
+    },
+]
diff --git a/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
new file mode 100644
index 0000000..9f98bf8
--- /dev/null
+++ b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
@@ -0,0 +1,70 @@
+[
+    FileSymbol {
+        name: "Foo",
+        def: Adt(
+            Struct(
+                Struct {
+                    id: StructId(
+                        3800,
+                    ),
+                },
+            ),
+        ),
+        loc: DeclarationLocation {
+            hir_file_id: FileId(
+                EditionedFileId(
+                    Id(2001),
+                ),
+            ),
+            ptr: SyntaxNodePtr {
+                kind: STRUCT,
+                range: 0..15,
+            },
+            name_ptr: AstPtr(
+                SyntaxNodePtr {
+                    kind: NAME,
+                    range: 11..14,
+                },
+            ),
+        },
+        container_name: None,
+        is_alias: false,
+        is_assoc: false,
+        is_import: false,
+        do_not_complete: Yes,
+    },
+    FileSymbol {
+        name: "Foo",
+        def: Adt(
+            Struct(
+                Struct {
+                    id: StructId(
+                        3800,
+                    ),
+                },
+            ),
+        ),
+        loc: DeclarationLocation {
+            hir_file_id: FileId(
+                EditionedFileId(
+                    Id(2000),
+                ),
+            ),
+            ptr: SyntaxNodePtr {
+                kind: USE_TREE,
+                range: 17..25,
+            },
+            name_ptr: AstPtr(
+                SyntaxNodePtr {
+                    kind: NAME_REF,
+                    range: 22..25,
+                },
+            ),
+        },
+        container_name: None,
+        is_alias: false,
+        is_assoc: false,
+        is_import: true,
+        do_not_complete: Yes,
+    },
+]
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
index fa7ba90..0e18ce9 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
@@ -127,4 +127,33 @@
 "#,
         )
     }
+
+    #[test]
+    fn impl_sized_for_unsized() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+trait Trait {
+    type Item
+    where
+        Self: Sized;
+
+    fn item()
+    where
+        Self: Sized;
+}
+
+trait OtherTrait {}
+
+impl Trait for () {
+    type Item = ();
+    fn item() {}
+}
+
+// Items with Self: Sized bound not required to be implemented for unsized types.
+impl Trait for str {}
+impl Trait for dyn OtherTrait {}
+ "#,
+        )
+    }
 }
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 1d19daf..2f8ed88 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -25,7 +25,7 @@
 smallvec.workspace = true
 triomphe.workspace = true
 nohash-hasher.workspace = true
-rustc_apfloat = "0.2.2"
+rustc_apfloat = "0.2.3"
 
 # local deps
 cfg.workspace = true
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 3d71da9..05196ac 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -10,6 +10,7 @@
     NavigationTarget, RunnableKind,
     annotations::fn_references::find_all_methods,
     goto_implementation::goto_implementation,
+    navigation_target,
     references::find_all_refs,
     runnables::{Runnable, runnables},
 };
@@ -148,15 +149,32 @@
             node: InFile<T>,
             source_file_id: FileId,
         ) -> Option<(TextRange, Option<TextRange>)> {
-            if let Some(InRealFile { file_id, value }) = node.original_ast_node_rooted(db) {
-                if file_id.file_id(db) == source_file_id {
-                    return Some((
-                        value.syntax().text_range(),
-                        value.name().map(|name| name.syntax().text_range()),
-                    ));
+            if let Some(name) = node.value.name().map(|name| name.syntax().text_range()) {
+                // if we have a name, try mapping that out of the macro expansion as we can put the
+                // annotation on that name token
+                // See `test_no_annotations_macro_struct_def` vs `test_annotations_macro_struct_def_call_site`
+                let res = navigation_target::orig_range_with_focus_r(
+                    db,
+                    node.file_id,
+                    node.value.syntax().text_range(),
+                    Some(name),
+                );
+                if res.call_site.0.file_id == source_file_id {
+                    if let Some(name_range) = res.call_site.1 {
+                        return Some((res.call_site.0.range, Some(name_range)));
+                    }
                 }
+            };
+            // otherwise try upmapping the entire node out of attributes
+            let InRealFile { file_id, value } = node.original_ast_node_rooted(db)?;
+            if file_id.file_id(db) == source_file_id {
+                Some((
+                    value.syntax().text_range(),
+                    value.name().map(|name| name.syntax().text_range()),
+                ))
+            } else {
+                None
             }
-            None
         }
     });
 
@@ -914,6 +932,56 @@
     }
 
     #[test]
+    fn test_annotations_macro_struct_def_call_site() {
+        check(
+            r#"
+//- /lib.rs
+macro_rules! m {
+    ($name:ident) => {
+        struct $name {}
+    };
+}
+
+m! {
+    Name
+};
+"#,
+            expect![[r#"
+                [
+                    Annotation {
+                        range: 83..87,
+                        kind: HasImpls {
+                            pos: FilePositionWrapper {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                offset: 83,
+                            },
+                            data: Some(
+                                [],
+                            ),
+                        },
+                    },
+                    Annotation {
+                        range: 83..87,
+                        kind: HasReferences {
+                            pos: FilePositionWrapper {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                offset: 83,
+                            },
+                            data: Some(
+                                [],
+                            ),
+                        },
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
     fn test_annotations_appear_above_whole_item_when_configured_to_do_so() {
         check_with_config(
             r#"
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index 38c032d..267e8ff 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -29,7 +29,7 @@
         .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
     let range = original_token.text_range();
     let info: Vec<NavigationTarget> = sema
-        .descend_into_macros_no_opaque(original_token)
+        .descend_into_macros_no_opaque(original_token, false)
         .iter()
         .filter_map(|token| {
             let parent = token.value.parent()?;
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 7917aab..574803f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -88,7 +88,7 @@
     }
 
     let navs = sema
-        .descend_into_macros_no_opaque(original_token.clone())
+        .descend_into_macros_no_opaque(original_token.clone(), false)
         .into_iter()
         .filter_map(|token| {
             let parent = token.value.parent()?;
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index a6c7ea2..9781e71 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -70,7 +70,7 @@
     }
 
     let range = token.text_range();
-    sema.descend_into_macros_no_opaque(token)
+    sema.descend_into_macros_no_opaque(token,false)
         .into_iter()
         .filter_map(|token| {
             sema
diff --git a/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/crates/ide/src/inlay_hints/implied_dyn_trait.rs
index 32d1305..cd01c07 100644
--- a/crates/ide/src/inlay_hints/implied_dyn_trait.rs
+++ b/crates/ide/src/inlay_hints/implied_dyn_trait.rs
@@ -22,9 +22,14 @@
             let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
             if ast::TypeBound::can_cast(parent.kind())
                 || ast::TypeAnchor::can_cast(parent.kind())
-                || ast::Impl::cast(parent)
-                    .and_then(|it| it.trait_())
-                    .is_some_and(|it| it.syntax() == path.syntax())
+                || ast::Impl::cast(parent).is_some_and(|it| {
+                    it.trait_().map_or(
+                        // only show it for impl type if the impl is not incomplete, otherwise we
+                        // are likely typing a trait impl
+                        it.assoc_item_list().is_none_or(|it| it.l_curly_token().is_none()),
+                        |trait_| trait_.syntax() == path.syntax(),
+                    )
+                })
             {
                 return None;
             }
@@ -85,6 +90,7 @@
   // ^ dyn
 impl T for (T) {}
         // ^^^ dyn
+impl T
 "#,
         );
     }
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 9334b73..4c7c597 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -867,7 +867,7 @@
             }
 
             // def site name
-            // FIXME: This can be de improved
+            // FIXME: This can be improved
             Some((focus_range, _ctxt)) => {
                 match value_range {
                     // but overall node is in macro input
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index ab13960..f48150b 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -5,11 +5,11 @@
 use cfg::{CfgAtom, CfgExpr};
 use hir::{
     AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, ModPath, Name, PathKind, Semantics,
-    Symbol, db::HirDatabase, sym, symbols::FxIndexSet,
+    Symbol, db::HirDatabase, sym,
 };
 use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::{
-    FilePosition, FxHashMap, FxIndexMap, RootDatabase, SymbolKind,
+    FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind,
     base_db::RootQueryDb,
     defs::Definition,
     documentation::docs_from_attrs,
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index fc922dd..d5cbb73 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -222,6 +222,7 @@
     fn_once_output,
     fn_once,
     async_fn_once,
+    async_fn_once_output,
     async_fn_mut,
     async_fn,
     fn_ptr_addr,
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 30e2d54..89b8631 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -2,7 +2,7 @@
 //! for incorporating changes.
 // Note, don't remove any public api from this. This API is consumed by external tools
 // to run rust-analyzer as a library.
-use std::{collections::hash_map::Entry, mem, path::Path, sync};
+use std::{any::Any, collections::hash_map::Entry, mem, path::Path, sync};
 
 use crossbeam_channel::{Receiver, unbounded};
 use hir_expand::proc_macro::{
@@ -512,6 +512,10 @@
             Err(err) => Err(ProcMacroExpansionError::System(err.to_string())),
         }
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        (other as &dyn Any).downcast_ref::<Self>() == Some(self)
+    }
 }
 
 #[cfg(test)]
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index db75dce..04ac85a 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -197,6 +197,10 @@
                                         builder.push(tt::Leaf::Punct(*it))
                                     }
                                 }
+                                Separator::Lifetime(punct, ident) => {
+                                    builder.push(tt::Leaf::Punct(*punct));
+                                    builder.push(tt::Leaf::Ident(ident.clone()));
+                                }
                             };
                         }
                     }
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 940aaac..a8d5965 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -823,7 +823,7 @@
                         "expected token tree",
                     )
                 }),
-                MetaVarKind::Lifetime => expect_lifetime(input).map_err(|()| {
+                MetaVarKind::Lifetime => expect_lifetime(input).map(drop).map_err(|()| {
                     ExpandError::binding_error(
                         span.unwrap_or(delim_span.close),
                         "expected lifetime",
@@ -963,6 +963,10 @@
             }
             Err(_) => false,
         },
+        Separator::Lifetime(_punct, ident) => match expect_lifetime(&mut fork) {
+            Ok(lifetime) => lifetime.sym == ident.sym,
+            Err(_) => false,
+        },
     };
     if ok {
         *iter = fork;
@@ -983,13 +987,12 @@
     Ok(())
 }
 
-fn expect_lifetime<S: Copy>(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
+fn expect_lifetime<'a, S: Copy>(iter: &mut TtIter<'a, S>) -> Result<&'a tt::Ident<S>, ()> {
     let punct = iter.expect_single_punct()?;
     if punct.char != '\'' {
         return Err(());
     }
-    iter.expect_ident_or_underscore()?;
-    Ok(())
+    iter.expect_ident_or_underscore()
 }
 
 fn eat_char<S: Copy>(iter: &mut TtIter<'_, S>, c: char) {
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index ec277ba..2c046df 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -497,6 +497,10 @@
                         builder.push(tt::Leaf::from(punct));
                     }
                 }
+                Separator::Lifetime(punct, ident) => {
+                    builder.push(tt::Leaf::from(*punct));
+                    builder.push(tt::Leaf::from(ident.clone()));
+                }
             };
         }
 
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index fbc353d..7111012 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -155,6 +155,7 @@
     Literal(tt::Literal<Span>),
     Ident(tt::Ident<Span>),
     Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>),
+    Lifetime(tt::Punct<Span>, tt::Ident<Span>),
 }
 
 // Note that when we compare a Separator, we just care about its textual value.
@@ -170,6 +171,7 @@
                 let b_iter = b.iter().map(|b| b.char);
                 a_iter.eq(b_iter)
             }
+            (Lifetime(_, a), Lifetime(_, b)) => a.sym == b.sym,
             _ => false,
         }
     }
@@ -350,10 +352,19 @@
             _ => true,
         };
         match tt {
-            tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => {
-                return Err(ParseError::InvalidRepeat);
-            }
-            tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()),
+            tt::Leaf::Ident(ident) => match separator {
+                Separator::Puncts(puncts) if puncts.is_empty() => {
+                    separator = Separator::Ident(ident.clone());
+                }
+                Separator::Puncts(puncts) => match puncts.as_slice() {
+                    [tt::Punct { char: '\'', .. }] => {
+                        separator = Separator::Lifetime(puncts[0], ident.clone());
+                    }
+                    _ => return Err(ParseError::InvalidRepeat),
+                },
+                _ => return Err(ParseError::InvalidRepeat),
+            },
+            tt::Leaf::Literal(_) if has_sep => return Err(ParseError::InvalidRepeat),
             tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()),
             tt::Leaf::Punct(punct) => {
                 let repeat_kind = match punct.char {
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 769455f..5603451 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -3,7 +3,9 @@
 // FIXME: Move more of the nameres independent tests from
 // crates\hir-def\src\macro_expansion_tests\mod.rs to this
 use expect_test::expect;
-use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContext};
+use span::{
+    Edition, EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext,
+};
 use stdx::format_to;
 use tt::{TextRange, TextSize};
 
@@ -24,7 +26,7 @@
         def_edition,
         SpanAnchor {
             file_id: EditionedFileId::new(FileId::from_raw(0), def_edition),
-            ast_id: ErasedFileAstId::from_raw(0),
+            ast_id: ROOT_ERASED_FILE_AST_ID,
         },
         SyntaxContext::root(Edition::CURRENT),
         decl,
@@ -37,7 +39,7 @@
     };
     let call_anchor = SpanAnchor {
         file_id: EditionedFileId::new(FileId::from_raw(1), call_edition),
-        ast_id: ErasedFileAstId::from_raw(0),
+        ast_id: ROOT_ERASED_FILE_AST_ID,
     };
     let arg_tt = syntax_bridge::parse_to_token_tree(
         call_edition,
@@ -110,8 +112,8 @@
 "#,
         r#""#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..0#ROOT2024 1:0@0..0#ROOT2024
-              SUBTREE {} 0:0@9..10#ROOT2024 0:0@11..12#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..0#ROOT2024 1:Root[0000, 0]@0..0#ROOT2024
+              SUBTREE {} 0:Root[0000, 0]@9..10#ROOT2024 0:Root[0000, 0]@11..12#ROOT2024
 
             {}"#]],
     );
@@ -133,25 +135,25 @@
 struct MyTraitMap2
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..20#ROOT2024 1:0@0..20#ROOT2024
-              IDENT   struct 0:0@34..40#ROOT2024
-              IDENT   MyTraitMap2 1:0@8..19#ROOT2024
-              SUBTREE {} 0:0@48..49#ROOT2024 0:0@100..101#ROOT2024
-                IDENT   map 0:0@58..61#ROOT2024
-                PUNCH   : [alone] 0:0@61..62#ROOT2024
-                PUNCH   : [joint] 0:0@63..64#ROOT2024
-                PUNCH   : [alone] 0:0@64..65#ROOT2024
-                IDENT   std 0:0@65..68#ROOT2024
-                PUNCH   : [joint] 0:0@68..69#ROOT2024
-                PUNCH   : [alone] 0:0@69..70#ROOT2024
-                IDENT   collections 0:0@70..81#ROOT2024
-                PUNCH   : [joint] 0:0@81..82#ROOT2024
-                PUNCH   : [alone] 0:0@82..83#ROOT2024
-                IDENT   HashSet 0:0@83..90#ROOT2024
-                PUNCH   < [alone] 0:0@90..91#ROOT2024
-                SUBTREE () 0:0@91..92#ROOT2024 0:0@92..93#ROOT2024
-                PUNCH   > [joint] 0:0@93..94#ROOT2024
-                PUNCH   , [alone] 0:0@94..95#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..20#ROOT2024 1:Root[0000, 0]@0..20#ROOT2024
+              IDENT   struct 0:Root[0000, 0]@34..40#ROOT2024
+              IDENT   MyTraitMap2 1:Root[0000, 0]@8..19#ROOT2024
+              SUBTREE {} 0:Root[0000, 0]@48..49#ROOT2024 0:Root[0000, 0]@100..101#ROOT2024
+                IDENT   map 0:Root[0000, 0]@58..61#ROOT2024
+                PUNCH   : [alone] 0:Root[0000, 0]@61..62#ROOT2024
+                PUNCH   : [joint] 0:Root[0000, 0]@63..64#ROOT2024
+                PUNCH   : [alone] 0:Root[0000, 0]@64..65#ROOT2024
+                IDENT   std 0:Root[0000, 0]@65..68#ROOT2024
+                PUNCH   : [joint] 0:Root[0000, 0]@68..69#ROOT2024
+                PUNCH   : [alone] 0:Root[0000, 0]@69..70#ROOT2024
+                IDENT   collections 0:Root[0000, 0]@70..81#ROOT2024
+                PUNCH   : [joint] 0:Root[0000, 0]@81..82#ROOT2024
+                PUNCH   : [alone] 0:Root[0000, 0]@82..83#ROOT2024
+                IDENT   HashSet 0:Root[0000, 0]@83..90#ROOT2024
+                PUNCH   < [alone] 0:Root[0000, 0]@90..91#ROOT2024
+                SUBTREE () 0:Root[0000, 0]@91..92#ROOT2024 0:Root[0000, 0]@92..93#ROOT2024
+                PUNCH   > [joint] 0:Root[0000, 0]@93..94#ROOT2024
+                PUNCH   , [alone] 0:Root[0000, 0]@94..95#ROOT2024
 
             struct MyTraitMap2 {
                 map: ::std::collections::HashSet<()>,
@@ -180,28 +182,28 @@
 }
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..63#ROOT2024 1:0@0..63#ROOT2024
-              IDENT   fn 1:0@1..3#ROOT2024
-              IDENT   main 1:0@4..8#ROOT2024
-              SUBTREE () 1:0@8..9#ROOT2024 1:0@9..10#ROOT2024
-              SUBTREE {} 1:0@11..12#ROOT2024 1:0@61..62#ROOT2024
-                LITERAL Integer 1 1:0@17..18#ROOT2024
-                PUNCH   ; [alone] 1:0@18..19#ROOT2024
-                LITERAL Float 1.0 1:0@24..27#ROOT2024
-                PUNCH   ; [alone] 1:0@27..28#ROOT2024
-                SUBTREE () 1:0@33..34#ROOT2024 1:0@39..40#ROOT2024
-                  SUBTREE () 1:0@34..35#ROOT2024 1:0@37..38#ROOT2024
-                    LITERAL Integer 1 1:0@35..36#ROOT2024
-                    PUNCH   , [alone] 1:0@36..37#ROOT2024
-                  PUNCH   , [alone] 1:0@38..39#ROOT2024
-                PUNCH   . [alone] 1:0@40..41#ROOT2024
-                LITERAL Float 0.0 1:0@41..44#ROOT2024
-                PUNCH   ; [alone] 1:0@44..45#ROOT2024
-                IDENT   let 1:0@50..53#ROOT2024
-                IDENT   x 1:0@54..55#ROOT2024
-                PUNCH   = [alone] 1:0@56..57#ROOT2024
-                LITERAL Integer 1 1:0@58..59#ROOT2024
-                PUNCH   ; [alone] 1:0@59..60#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..63#ROOT2024 1:Root[0000, 0]@0..63#ROOT2024
+              IDENT   fn 1:Root[0000, 0]@1..3#ROOT2024
+              IDENT   main 1:Root[0000, 0]@4..8#ROOT2024
+              SUBTREE () 1:Root[0000, 0]@8..9#ROOT2024 1:Root[0000, 0]@9..10#ROOT2024
+              SUBTREE {} 1:Root[0000, 0]@11..12#ROOT2024 1:Root[0000, 0]@61..62#ROOT2024
+                LITERAL Integer 1 1:Root[0000, 0]@17..18#ROOT2024
+                PUNCH   ; [alone] 1:Root[0000, 0]@18..19#ROOT2024
+                LITERAL Float 1.0 1:Root[0000, 0]@24..27#ROOT2024
+                PUNCH   ; [alone] 1:Root[0000, 0]@27..28#ROOT2024
+                SUBTREE () 1:Root[0000, 0]@33..34#ROOT2024 1:Root[0000, 0]@39..40#ROOT2024
+                  SUBTREE () 1:Root[0000, 0]@34..35#ROOT2024 1:Root[0000, 0]@37..38#ROOT2024
+                    LITERAL Integer 1 1:Root[0000, 0]@35..36#ROOT2024
+                    PUNCH   , [alone] 1:Root[0000, 0]@36..37#ROOT2024
+                  PUNCH   , [alone] 1:Root[0000, 0]@38..39#ROOT2024
+                PUNCH   . [alone] 1:Root[0000, 0]@40..41#ROOT2024
+                LITERAL Float 0.0 1:Root[0000, 0]@41..44#ROOT2024
+                PUNCH   ; [alone] 1:Root[0000, 0]@44..45#ROOT2024
+                IDENT   let 1:Root[0000, 0]@50..53#ROOT2024
+                IDENT   x 1:Root[0000, 0]@54..55#ROOT2024
+                PUNCH   = [alone] 1:Root[0000, 0]@56..57#ROOT2024
+                LITERAL Integer 1 1:Root[0000, 0]@58..59#ROOT2024
+                PUNCH   ; [alone] 1:Root[0000, 0]@59..60#ROOT2024
 
             fn main(){
                 1;
@@ -227,14 +229,14 @@
     const { 1 },
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..25#ROOT2024 1:0@0..25#ROOT2024
-              IDENT   _ 1:0@5..6#ROOT2024
-              PUNCH   ; [joint] 0:0@36..37#ROOT2024
-              SUBTREE () 0:0@34..35#ROOT2024 0:0@34..35#ROOT2024
-                IDENT   const 1:0@12..17#ROOT2024
-                SUBTREE {} 1:0@18..19#ROOT2024 1:0@22..23#ROOT2024
-                  LITERAL Integer 1 1:0@20..21#ROOT2024
-              PUNCH   ; [alone] 0:0@39..40#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..25#ROOT2024 1:Root[0000, 0]@0..25#ROOT2024
+              IDENT   _ 1:Root[0000, 0]@5..6#ROOT2024
+              PUNCH   ; [joint] 0:Root[0000, 0]@36..37#ROOT2024
+              SUBTREE () 0:Root[0000, 0]@34..35#ROOT2024 0:Root[0000, 0]@34..35#ROOT2024
+                IDENT   const 1:Root[0000, 0]@12..17#ROOT2024
+                SUBTREE {} 1:Root[0000, 0]@18..19#ROOT2024 1:Root[0000, 0]@22..23#ROOT2024
+                  LITERAL Integer 1 1:Root[0000, 0]@20..21#ROOT2024
+              PUNCH   ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
 
             _;
             (const  {
@@ -255,13 +257,13 @@
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..6#ROOT2024,
+                    1:Root[0000, 0]@5..6#ROOT2024,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..8#ROOT2024 1:0@0..8#ROOT2024
-              PUNCH   ; [alone] 0:0@39..40#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..8#ROOT2024 1:Root[0000, 0]@0..8#ROOT2024
+              PUNCH   ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
 
             ;"#]],
     );
@@ -279,13 +281,13 @@
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..10#ROOT2024,
+                    1:Root[0000, 0]@5..10#ROOT2024,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..18#ROOT2024 1:0@0..18#ROOT2024
-              PUNCH   ; [alone] 0:0@39..40#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..18#ROOT2024 1:Root[0000, 0]@0..18#ROOT2024
+              PUNCH   ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
 
             ;"#]],
     );
@@ -305,26 +307,26 @@
     break 'foo bar,
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..76#ROOT2024 1:0@0..76#ROOT2024
-              LITERAL Integer 4 1:0@5..6#ROOT2024
-              PUNCH   ; [joint] 0:0@41..42#ROOT2024
-              LITERAL Str literal 1:0@12..21#ROOT2024
-              PUNCH   ; [joint] 0:0@41..42#ROOT2024
-              SUBTREE () 0:0@39..40#ROOT2024 0:0@39..40#ROOT2024
-                IDENT   funcall 1:0@27..34#ROOT2024
-                SUBTREE () 1:0@34..35#ROOT2024 1:0@35..36#ROOT2024
-              PUNCH   ; [joint] 0:0@41..42#ROOT2024
-              SUBTREE () 0:0@39..40#ROOT2024 0:0@39..40#ROOT2024
-                IDENT   future 1:0@42..48#ROOT2024
-                PUNCH   . [alone] 1:0@48..49#ROOT2024
-                IDENT   await 1:0@49..54#ROOT2024
-              PUNCH   ; [joint] 0:0@41..42#ROOT2024
-              SUBTREE () 0:0@39..40#ROOT2024 0:0@39..40#ROOT2024
-                IDENT   break 1:0@60..65#ROOT2024
-                PUNCH   ' [joint] 1:0@66..67#ROOT2024
-                IDENT   foo 1:0@67..70#ROOT2024
-                IDENT   bar 1:0@71..74#ROOT2024
-              PUNCH   ; [alone] 0:0@44..45#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..76#ROOT2024 1:Root[0000, 0]@0..76#ROOT2024
+              LITERAL Integer 4 1:Root[0000, 0]@5..6#ROOT2024
+              PUNCH   ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+              LITERAL Str literal 1:Root[0000, 0]@12..21#ROOT2024
+              PUNCH   ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+              SUBTREE () 0:Root[0000, 0]@39..40#ROOT2024 0:Root[0000, 0]@39..40#ROOT2024
+                IDENT   funcall 1:Root[0000, 0]@27..34#ROOT2024
+                SUBTREE () 1:Root[0000, 0]@34..35#ROOT2024 1:Root[0000, 0]@35..36#ROOT2024
+              PUNCH   ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+              SUBTREE () 0:Root[0000, 0]@39..40#ROOT2024 0:Root[0000, 0]@39..40#ROOT2024
+                IDENT   future 1:Root[0000, 0]@42..48#ROOT2024
+                PUNCH   . [alone] 1:Root[0000, 0]@48..49#ROOT2024
+                IDENT   await 1:Root[0000, 0]@49..54#ROOT2024
+              PUNCH   ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+              SUBTREE () 0:Root[0000, 0]@39..40#ROOT2024 0:Root[0000, 0]@39..40#ROOT2024
+                IDENT   break 1:Root[0000, 0]@60..65#ROOT2024
+                PUNCH   ' [joint] 1:Root[0000, 0]@66..67#ROOT2024
+                IDENT   foo 1:Root[0000, 0]@67..70#ROOT2024
+                IDENT   bar 1:Root[0000, 0]@71..74#ROOT2024
+              PUNCH   ; [alone] 0:Root[0000, 0]@44..45#ROOT2024
 
             4;
             "literal";
@@ -346,13 +348,13 @@
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..6#ROOT2024,
+                    1:Root[0000, 0]@5..6#ROOT2024,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..8#ROOT2024 1:0@0..8#ROOT2024
-              PUNCH   ; [alone] 0:0@44..45#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..8#ROOT2024 1:Root[0000, 0]@0..8#ROOT2024
+              PUNCH   ; [alone] 0:Root[0000, 0]@44..45#ROOT2024
 
             ;"#]],
     );
@@ -370,88 +372,88 @@
     check(
         "-1",
         expect![[r#"
-            SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024
-              PUNCH   - [alone] 0:0@10..11#ROOT2024
-              LITERAL Integer 1 0:0@11..12#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024
+              PUNCH   - [alone] 0:Root[0000, 0]@10..11#ROOT2024
+              LITERAL Integer 1 0:Root[0000, 0]@11..12#ROOT2024
 
             -1"#]],
     );
     check(
         "- 1",
         expect![[r#"
-            SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024
-              PUNCH   - [alone] 0:0@10..11#ROOT2024
-              LITERAL Integer 1 0:0@11..12#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..3#ROOT2024 1:Root[0000, 0]@0..3#ROOT2024
+              PUNCH   - [alone] 0:Root[0000, 0]@10..11#ROOT2024
+              LITERAL Integer 1 0:Root[0000, 0]@11..12#ROOT2024
 
             -1"#]],
     );
     check(
         "-2",
         expect![[r#"
-            SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024
-              PUNCH   - [alone] 0:0@25..26#ROOT2024
-              LITERAL Integer 2 0:0@27..28#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024
+              PUNCH   - [alone] 0:Root[0000, 0]@25..26#ROOT2024
+              LITERAL Integer 2 0:Root[0000, 0]@27..28#ROOT2024
 
             -2"#]],
     );
     check(
         "- 2",
         expect![[r#"
-            SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024
-              PUNCH   - [alone] 0:0@25..26#ROOT2024
-              LITERAL Integer 2 0:0@27..28#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..3#ROOT2024 1:Root[0000, 0]@0..3#ROOT2024
+              PUNCH   - [alone] 0:Root[0000, 0]@25..26#ROOT2024
+              LITERAL Integer 2 0:Root[0000, 0]@27..28#ROOT2024
 
             -2"#]],
     );
     check(
         "-3.0",
         expect![[r#"
-            SUBTREE $$ 1:0@0..4#ROOT2024 1:0@0..4#ROOT2024
-              PUNCH   - [alone] 0:0@43..44#ROOT2024
-              LITERAL Float 3.0 0:0@45..48#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..4#ROOT2024 1:Root[0000, 0]@0..4#ROOT2024
+              PUNCH   - [alone] 0:Root[0000, 0]@43..44#ROOT2024
+              LITERAL Float 3.0 0:Root[0000, 0]@45..48#ROOT2024
 
             -3.0"#]],
     );
     check(
         "- 3.0",
         expect![[r#"
-            SUBTREE $$ 1:0@0..5#ROOT2024 1:0@0..5#ROOT2024
-              PUNCH   - [alone] 0:0@43..44#ROOT2024
-              LITERAL Float 3.0 0:0@45..48#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..5#ROOT2024 1:Root[0000, 0]@0..5#ROOT2024
+              PUNCH   - [alone] 0:Root[0000, 0]@43..44#ROOT2024
+              LITERAL Float 3.0 0:Root[0000, 0]@45..48#ROOT2024
 
             -3.0"#]],
     );
     check(
         "@1",
         expect![[r#"
-            SUBTREE $$ 1:0@0..2#ROOT2024 1:0@0..2#ROOT2024
-              LITERAL Integer 1 1:0@1..2#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024
+              LITERAL Integer 1 1:Root[0000, 0]@1..2#ROOT2024
 
             1"#]],
     );
     check(
         "@-1",
         expect![[r#"
-            SUBTREE $$ 1:0@0..3#ROOT2024 1:0@0..3#ROOT2024
-              PUNCH   - [alone] 1:0@1..2#ROOT2024
-              LITERAL Integer 1 1:0@2..3#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..3#ROOT2024 1:Root[0000, 0]@0..3#ROOT2024
+              PUNCH   - [alone] 1:Root[0000, 0]@1..2#ROOT2024
+              LITERAL Integer 1 1:Root[0000, 0]@2..3#ROOT2024
 
             -1"#]],
     );
     check(
         "@1.0",
         expect![[r#"
-            SUBTREE $$ 1:0@0..4#ROOT2024 1:0@0..4#ROOT2024
-              LITERAL Float 1.0 1:0@1..4#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..4#ROOT2024 1:Root[0000, 0]@0..4#ROOT2024
+              LITERAL Float 1.0 1:Root[0000, 0]@1..4#ROOT2024
 
             1.0"#]],
     );
     check(
         "@-1.0",
         expect![[r#"
-            SUBTREE $$ 1:0@0..5#ROOT2024 1:0@0..5#ROOT2024
-              PUNCH   - [alone] 1:0@1..2#ROOT2024
-              LITERAL Float 1.0 1:0@2..5#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..5#ROOT2024 1:Root[0000, 0]@0..5#ROOT2024
+              PUNCH   - [alone] 1:Root[0000, 0]@1..2#ROOT2024
+              LITERAL Float 1.0 1:Root[0000, 0]@2..5#ROOT2024
 
             -1.0"#]],
     );
@@ -460,16 +462,16 @@
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@1..2#ROOT2024,
+                    1:Root[0000, 0]@1..2#ROOT2024,
                     BindingError(
                         "expected literal",
                     ),
                 ),
             }
 
-            SUBTREE $$ 1:0@0..6#ROOT2024 1:0@0..6#ROOT2024
-              PUNCH   - [joint] 1:0@1..2#ROOT2024
-              PUNCH   - [alone] 1:0@2..3#ROOT2024
+            SUBTREE $$ 1:Root[0000, 0]@0..6#ROOT2024 1:Root[0000, 0]@0..6#ROOT2024
+              PUNCH   - [joint] 1:Root[0000, 0]@1..2#ROOT2024
+              PUNCH   - [alone] 1:Root[0000, 0]@2..3#ROOT2024
 
             --"#]],
     );
diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs
index 55185aa..1659362 100644
--- a/crates/proc-macro-api/src/legacy_protocol/msg.rs
+++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -22,9 +22,10 @@
 pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
 /// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field.
 pub const EXTENDED_LEAF_DATA: u32 = 5;
+pub const HASHED_AST_ID: u32 = 6;
 
 /// Current API version of the proc-macro protocol.
-pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA;
+pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID;
 
 /// Represents requests sent from the client to the proc-macro-srv.
 #[derive(Debug, Serialize, Deserialize)]
@@ -201,7 +202,9 @@
 #[cfg(test)]
 mod tests {
     use intern::{Symbol, sym};
-    use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContext, TextRange, TextSize};
+    use span::{
+        Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize,
+    };
     use tt::{
         Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
         TopSubtreeBuilder,
@@ -215,7 +218,7 @@
                 span::FileId::from_raw(0xe4e4e),
                 span::Edition::CURRENT,
             ),
-            ast_id: ErasedFileAstId::from_raw(0),
+            ast_id: ROOT_ERASED_FILE_AST_ID,
         };
 
         let mut builder = TopSubtreeBuilder::new(Delimiter {
diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs
index 25c30b6..516c741 100644
--- a/crates/proc-macro-api/src/lib.rs
+++ b/crates/proc-macro-api/src/lib.rs
@@ -12,13 +12,13 @@
 mod process;
 
 use paths::{AbsPath, AbsPathBuf};
-use span::Span;
+use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
 use std::{fmt, io, sync::Arc, time::SystemTime};
 
 use crate::{
     legacy_protocol::msg::{
-        ExpandMacro, ExpandMacroData, ExpnGlobals, FlatTree, HAS_GLOBAL_SPANS, PanicMessage,
-        RUST_ANALYZER_SPAN_SUPPORT, Request, Response, SpanDataIndexMap,
+        ExpandMacro, ExpandMacroData, ExpnGlobals, FlatTree, HAS_GLOBAL_SPANS, HASHED_AST_ID,
+        PanicMessage, RUST_ANALYZER_SPAN_SUPPORT, Request, Response, SpanDataIndexMap,
         deserialize_span_data_index_map, flat::serialize_span_data_index_map,
     },
     process::ProcMacroServerProcess,
@@ -161,6 +161,38 @@
         self.kind
     }
 
+    fn needs_fixup_change(&self) -> bool {
+        let version = self.process.version();
+        (RUST_ANALYZER_SPAN_SUPPORT..HASHED_AST_ID).contains(&version)
+    }
+
+    /// On some server versions, the fixup ast id is different than ours. So change it to match.
+    fn change_fixup_to_match_old_server(&self, tt: &mut tt::TopSubtree<Span>) {
+        const OLD_FIXUP_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(!0 - 1);
+        let change_ast_id = |ast_id: &mut ErasedFileAstId| {
+            if *ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
+                *ast_id = OLD_FIXUP_AST_ID;
+            } else if *ast_id == OLD_FIXUP_AST_ID {
+                // Swap between them, that means no collision plus the change can be reversed by doing itself.
+                *ast_id = FIXUP_ERASED_FILE_AST_ID_MARKER;
+            }
+        };
+
+        for tt in &mut tt.0 {
+            match tt {
+                tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { span, .. }))
+                | tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { span, .. }))
+                | tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { span, .. })) => {
+                    change_ast_id(&mut span.anchor.ast_id);
+                }
+                tt::TokenTree::Subtree(subtree) => {
+                    change_ast_id(&mut subtree.delimiter.open.anchor.ast_id);
+                    change_ast_id(&mut subtree.delimiter.close.anchor.ast_id);
+                }
+            }
+        }
+    }
+
     /// Expands the procedural macro by sending an expansion request to the server.
     /// This includes span information and environmental context.
     pub fn expand(
@@ -173,6 +205,20 @@
         mixed_site: Span,
         current_dir: String,
     ) -> Result<Result<tt::TopSubtree<Span>, PanicMessage>, ServerError> {
+        let (mut subtree, mut attr) = (subtree, attr);
+        let (mut subtree_changed, mut attr_changed);
+        if self.needs_fixup_change() {
+            subtree_changed = tt::TopSubtree::from_subtree(subtree);
+            self.change_fixup_to_match_old_server(&mut subtree_changed);
+            subtree = subtree_changed.view();
+
+            if let Some(attr) = &mut attr {
+                attr_changed = tt::TopSubtree::from_subtree(*attr);
+                self.change_fixup_to_match_old_server(&mut attr_changed);
+                *attr = attr_changed.view();
+            }
+        }
+
         let version = self.process.version();
 
         let mut span_data_table = SpanDataIndexMap::default();
@@ -205,15 +251,23 @@
         let response = self.process.send_task(Request::ExpandMacro(Box::new(task)))?;
 
         match response {
-            Response::ExpandMacro(it) => {
-                Ok(it.map(|tree| FlatTree::to_subtree_resolved(tree, version, &span_data_table)))
-            }
+            Response::ExpandMacro(it) => Ok(it.map(|tree| {
+                let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
+                if self.needs_fixup_change() {
+                    self.change_fixup_to_match_old_server(&mut expanded);
+                }
+                expanded
+            })),
             Response::ExpandMacroExtended(it) => Ok(it.map(|resp| {
-                FlatTree::to_subtree_resolved(
+                let mut expanded = FlatTree::to_subtree_resolved(
                     resp.tree,
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
-                )
+                );
+                if self.needs_fixup_change() {
+                    self.change_fixup_to_match_old_server(&mut expanded);
+                }
+                expanded
             })),
             _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }),
         }
diff --git a/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/Cargo.toml
index eddefb3..c416d99 100644
--- a/crates/proc-macro-srv/proc-macro-test/Cargo.toml
+++ b/crates/proc-macro-srv/proc-macro-test/Cargo.toml
@@ -9,4 +9,4 @@
 [lib]
 
 [build-dependencies]
-cargo_metadata = "0.19.2"
+cargo_metadata = "0.20.0"
diff --git a/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
index 6820e4b..2a72e50 100644
--- a/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
+++ b/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
@@ -31,6 +31,7 @@
         TokenTree::from(Literal::byte_string(b"byte_string")),
         TokenTree::from(Literal::character('c')),
         TokenTree::from(Literal::string("string")),
+        TokenTree::from(Literal::string("-string")),
         TokenTree::from(Literal::c_string(c"cstring")),
         // as of 2022-07-21, there's no method on `Literal` to build a raw
         // string or a raw byte string
diff --git a/crates/proc-macro-srv/src/server_impl.rs b/crates/proc-macro-srv/src/server_impl.rs
index ad28599..dd576f2 100644
--- a/crates/proc-macro-srv/src/server_impl.rs
+++ b/crates/proc-macro-srv/src/server_impl.rs
@@ -199,37 +199,29 @@
         }
 
         bridge::TokenTree::Literal(literal) => {
-            let token_trees =
-                if let Some((_minus, symbol)) = literal.symbol.as_str().split_once('-') {
-                    let punct = tt::Punct {
-                        spacing: tt::Spacing::Alone,
-                        span: literal.span,
-                        char: '-' as char,
-                    };
-                    let leaf: tt::Leaf<Span> = tt::Leaf::from(punct);
-                    let minus_tree = tt::TokenTree::from(leaf);
-
-                    let literal = tt::Literal {
-                        symbol: Symbol::intern(symbol),
-                        suffix: literal.suffix,
-                        span: literal.span,
-                        kind: literal_kind_to_internal(literal.kind),
-                    };
-                    let leaf: tt::Leaf<Span> = tt::Leaf::from(literal);
-                    let tree = tt::TokenTree::from(leaf);
-                    vec![minus_tree, tree]
-                } else {
-                    let literal = tt::Literal {
-                        symbol: literal.symbol,
-                        suffix: literal.suffix,
-                        span: literal.span,
-                        kind: literal_kind_to_internal(literal.kind),
-                    };
-
-                    let leaf: tt::Leaf<Span> = tt::Leaf::from(literal);
-                    let tree = tt::TokenTree::from(leaf);
-                    vec![tree]
-                };
+            let mut token_trees = Vec::new();
+            let mut symbol = literal.symbol;
+            if matches!(
+                literal.kind,
+                proc_macro::bridge::LitKind::Integer | proc_macro::bridge::LitKind::Float
+            ) && symbol.as_str().starts_with('-')
+            {
+                token_trees.push(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
+                    spacing: tt::Spacing::Alone,
+                    span: literal.span,
+                    char: '-' as char,
+                })));
+                symbol = Symbol::intern(&symbol.as_str()[1..]);
+            }
+            let literal = tt::Literal {
+                symbol,
+                suffix: literal.suffix,
+                span: literal.span,
+                kind: literal_kind_to_internal(literal.kind),
+            };
+            let leaf: tt::Leaf<Span> = tt::Leaf::from(literal);
+            let tree = tt::TokenTree::from(leaf);
+            token_trees.push(tree);
             TokenStream { token_trees }
         }
 
diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 5d1271b..a1863ef 100644
--- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -318,7 +318,7 @@
             range: TextRange::empty(TextSize::new(0)),
             anchor: span::SpanAnchor {
                 file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
-                ast_id: span::ErasedFileAstId::from_raw(0),
+                ast_id: span::ROOT_ERASED_FILE_AST_ID,
             },
             ctx: SyntaxContext::root(span::Edition::CURRENT),
         };
@@ -360,7 +360,7 @@
             range: TextRange::empty(TextSize::new(0)),
             anchor: span::SpanAnchor {
                 file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
-                ast_id: span::ErasedFileAstId::from_raw(0),
+                ast_id: span::ROOT_ERASED_FILE_AST_ID,
             },
             ctx: SyntaxContext::root(span::Edition::CURRENT),
         };
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index 3a6ce63..08495f5 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -21,14 +21,14 @@
 
             SUBTREE $$ 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   struct 42:2@0..6#ROOT2024
-              IDENT   S 42:2@7..8#ROOT2024
-              PUNCH   ; [alone] 42:2@8..9#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   struct 42:Root[0000, 0]@0..6#ROOT2024
+              IDENT   S 42:Root[0000, 0]@7..8#ROOT2024
+              PUNCH   ; [alone] 42:Root[0000, 0]@8..9#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024"#]],
     );
 }
 
@@ -52,19 +52,19 @@
                 LITERAL Str #[derive(DeriveError)] struct S ; 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   struct 42:2@0..6#ROOT2024
-              IDENT   S 42:2@7..8#ROOT2024
-              PUNCH   ; [alone] 42:2@8..9#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   struct 42:Root[0000, 0]@0..6#ROOT2024
+              IDENT   S 42:Root[0000, 0]@7..8#ROOT2024
+              PUNCH   ; [alone] 42:Root[0000, 0]@8..9#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   compile_error 42:2@0..100#ROOT2024
-              PUNCH   ! [alone] 42:2@0..100#ROOT2024
-              SUBTREE () 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#ROOT2024
-              PUNCH   ; [alone] 42:2@0..100#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   compile_error 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   ! [alone] 42:Root[0000, 0]@0..100#ROOT2024
+              SUBTREE () 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+                LITERAL Str #[derive(DeriveError)] struct S ; 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   ; [alone] 42:Root[0000, 0]@0..100#ROOT2024"#]],
     );
 }
 
@@ -94,25 +94,25 @@
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   ident 42:2@0..5#ROOT2024
-              PUNCH   , [alone] 42:2@5..6#ROOT2024
-              LITERAL Integer 0 42:2@7..8#ROOT2024
-              PUNCH   , [alone] 42:2@8..9#ROOT2024
-              LITERAL Integer 1 42:2@10..11#ROOT2024
-              PUNCH   , [alone] 42:2@11..12#ROOT2024
-              SUBTREE [] 42:2@13..14#ROOT2024 42:2@14..15#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   ident 42:Root[0000, 0]@0..5#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+              LITERAL Integer 0 42:Root[0000, 0]@7..8#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@8..9#ROOT2024
+              LITERAL Integer 1 42:Root[0000, 0]@10..11#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+              SUBTREE [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   ident 42:2@0..5#ROOT2024
-              PUNCH   , [alone] 42:2@5..6#ROOT2024
-              LITERAL Integer 0 42:2@7..8#ROOT2024
-              PUNCH   , [alone] 42:2@8..9#ROOT2024
-              LITERAL Integer 1 42:2@10..11#ROOT2024
-              PUNCH   , [alone] 42:2@11..12#ROOT2024
-              SUBTREE [] 42:2@13..14#ROOT2024 42:2@14..15#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   ident 42:Root[0000, 0]@0..5#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+              LITERAL Integer 0 42:Root[0000, 0]@7..8#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@8..9#ROOT2024
+              LITERAL Integer 1 42:Root[0000, 0]@10..11#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+              SUBTREE [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024"#]],
     );
 }
 
@@ -134,17 +134,17 @@
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   ident 42:2@0..5#ROOT2024
-              PUNCH   , [alone] 42:2@5..6#ROOT2024
-              SUBTREE [] 42:2@7..8#ROOT2024 42:2@8..9#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   ident 42:Root[0000, 0]@0..5#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+              SUBTREE [] 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@8..9#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   ident 42:2@0..5#ROOT2024
-              PUNCH   , [alone] 42:2@5..6#ROOT2024
-              SUBTREE [] 42:2@7..9#ROOT2024 42:2@7..9#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   ident 42:Root[0000, 0]@0..5#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+              SUBTREE [] 42:Root[0000, 0]@7..9#ROOT2024 42:Root[0000, 0]@7..9#ROOT2024"#]],
     );
 }
 
@@ -162,13 +162,13 @@
             SUBTREE $$ 1 1
               IDENT   r#async 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   r#async 42:2@0..7#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   r#async 42:Root[0000, 0]@0..7#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   r#async 42:2@0..7#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   r#async 42:Root[0000, 0]@0..7#ROOT2024"#]],
     );
 }
 
@@ -187,14 +187,14 @@
             SUBTREE $$ 1 1
               IDENT   r#joined 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   foo 42:2@0..3#ROOT2024
-              IDENT   bar 42:2@8..11#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   foo 42:Root[0000, 0]@0..3#ROOT2024
+              IDENT   bar 42:Root[0000, 0]@8..11#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   r#joined 42:2@0..11#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   r#joined 42:Root[0000, 0]@0..11#ROOT2024"#]],
     );
 }
 
@@ -216,17 +216,17 @@
               IDENT   resolved_at_def_site 1
               IDENT   start_span 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   set_def_site 42:2@0..12#ROOT2024
-              IDENT   resolved_at_def_site 42:2@13..33#ROOT2024
-              IDENT   start_span 42:2@34..44#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   set_def_site 42:Root[0000, 0]@0..12#ROOT2024
+              IDENT   resolved_at_def_site 42:Root[0000, 0]@13..33#ROOT2024
+              IDENT   start_span 42:Root[0000, 0]@34..44#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   set_def_site 41:1@0..150#ROOT2024
-              IDENT   resolved_at_def_site 42:2@13..33#ROOT2024
-              IDENT   start_span 42:2@34..34#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   set_def_site 41:Root[0000, 0]@0..150#ROOT2024
+              IDENT   resolved_at_def_site 42:Root[0000, 0]@13..33#ROOT2024
+              IDENT   start_span 42:Root[0000, 0]@34..34#ROOT2024"#]],
     );
 }
 
@@ -244,6 +244,7 @@
               LITERAL ByteStr byte_string 1
               LITERAL Char c 1
               LITERAL Str string 1
+              LITERAL Str -string 1
               LITERAL CStr cstring 1
               LITERAL Float 3.14f64 1
               PUNCH   - [alone] 1
@@ -258,27 +259,28 @@
               PUNCH   - [alone] 1
               LITERAL Integer 123 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              LITERAL ByteStr byte_string 42:2@0..100#ROOT2024
-              LITERAL Char c 42:2@0..100#ROOT2024
-              LITERAL Str string 42:2@0..100#ROOT2024
-              LITERAL CStr cstring 42:2@0..100#ROOT2024
-              LITERAL Float 3.14f64 42:2@0..100#ROOT2024
-              PUNCH   - [alone] 42:2@0..100#ROOT2024
-              LITERAL Float 3.14f64 42:2@0..100#ROOT2024
-              LITERAL Float 3.14 42:2@0..100#ROOT2024
-              PUNCH   - [alone] 42:2@0..100#ROOT2024
-              LITERAL Float 3.14 42:2@0..100#ROOT2024
-              LITERAL Integer 123i64 42:2@0..100#ROOT2024
-              PUNCH   - [alone] 42:2@0..100#ROOT2024
-              LITERAL Integer 123i64 42:2@0..100#ROOT2024
-              LITERAL Integer 123 42:2@0..100#ROOT2024
-              PUNCH   - [alone] 42:2@0..100#ROOT2024
-              LITERAL Integer 123 42:2@0..100#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL ByteStr byte_string 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Char c 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Str string 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Str -string 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL CStr cstring 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Float 3.14f64 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Float 3.14f64 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Float 3.14 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Float 3.14 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Integer 123i64 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Integer 123i64 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Integer 123 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Integer 123 42:Root[0000, 0]@0..100#ROOT2024"#]],
     );
 }
 
@@ -296,13 +298,13 @@
               IDENT   standard 1
               IDENT   r#raw 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   standard 42:2@0..100#ROOT2024
-              IDENT   r#raw 42:2@0..100#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   standard 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   r#raw 42:Root[0000, 0]@0..100#ROOT2024"#]],
     );
 }
 
@@ -358,51 +360,51 @@
               PUNCH   , [alone] 1
               LITERAL CStr null 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              LITERAL Integer 1u16 42:2@0..4#ROOT2024
-              PUNCH   , [alone] 42:2@4..5#ROOT2024
-              LITERAL Integer 2_u32 42:2@6..11#ROOT2024
-              PUNCH   , [alone] 42:2@11..12#ROOT2024
-              PUNCH   - [alone] 42:2@13..14#ROOT2024
-              LITERAL Integer 4i64 42:2@14..18#ROOT2024
-              PUNCH   , [alone] 42:2@18..19#ROOT2024
-              LITERAL Float 3.14f32 42:2@20..27#ROOT2024
-              PUNCH   , [alone] 42:2@27..28#ROOT2024
-              LITERAL Str hello bridge 42:2@29..43#ROOT2024
-              PUNCH   , [alone] 42:2@43..44#ROOT2024
-              LITERAL Str suffixedsuffix 42:2@45..61#ROOT2024
-              PUNCH   , [alone] 42:2@61..62#ROOT2024
-              LITERAL StrRaw(2) raw 42:2@63..73#ROOT2024
-              PUNCH   , [alone] 42:2@73..74#ROOT2024
-              LITERAL Char a 42:2@75..78#ROOT2024
-              PUNCH   , [alone] 42:2@78..79#ROOT2024
-              LITERAL Byte b 42:2@80..84#ROOT2024
-              PUNCH   , [alone] 42:2@84..85#ROOT2024
-              LITERAL CStr null 42:2@86..93#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Integer 1u16 42:Root[0000, 0]@0..4#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@4..5#ROOT2024
+              LITERAL Integer 2_u32 42:Root[0000, 0]@6..11#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@13..14#ROOT2024
+              LITERAL Integer 4i64 42:Root[0000, 0]@14..18#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@18..19#ROOT2024
+              LITERAL Float 3.14f32 42:Root[0000, 0]@20..27#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@27..28#ROOT2024
+              LITERAL Str hello bridge 42:Root[0000, 0]@29..43#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@43..44#ROOT2024
+              LITERAL Str suffixedsuffix 42:Root[0000, 0]@45..61#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@61..62#ROOT2024
+              LITERAL StrRaw(2) raw 42:Root[0000, 0]@63..73#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@73..74#ROOT2024
+              LITERAL Char a 42:Root[0000, 0]@75..78#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@78..79#ROOT2024
+              LITERAL Byte b 42:Root[0000, 0]@80..84#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@84..85#ROOT2024
+              LITERAL CStr null 42:Root[0000, 0]@86..93#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              LITERAL Integer 1u16 42:2@0..4#ROOT2024
-              PUNCH   , [alone] 42:2@4..5#ROOT2024
-              LITERAL Integer 2_u32 42:2@6..11#ROOT2024
-              PUNCH   , [alone] 42:2@11..12#ROOT2024
-              PUNCH   - [alone] 42:2@13..14#ROOT2024
-              LITERAL Integer 4i64 42:2@14..18#ROOT2024
-              PUNCH   , [alone] 42:2@18..19#ROOT2024
-              LITERAL Float 3.14f32 42:2@20..27#ROOT2024
-              PUNCH   , [alone] 42:2@27..28#ROOT2024
-              LITERAL Str hello bridge 42:2@29..43#ROOT2024
-              PUNCH   , [alone] 42:2@43..44#ROOT2024
-              LITERAL Str suffixedsuffix 42:2@45..61#ROOT2024
-              PUNCH   , [alone] 42:2@61..62#ROOT2024
-              LITERAL StrRaw(2) raw 42:2@63..73#ROOT2024
-              PUNCH   , [alone] 42:2@73..74#ROOT2024
-              LITERAL Char a 42:2@75..78#ROOT2024
-              PUNCH   , [alone] 42:2@78..79#ROOT2024
-              LITERAL Byte b 42:2@80..84#ROOT2024
-              PUNCH   , [alone] 42:2@84..85#ROOT2024
-              LITERAL CStr null 42:2@86..93#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              LITERAL Integer 1u16 42:Root[0000, 0]@0..4#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@4..5#ROOT2024
+              LITERAL Integer 2_u32 42:Root[0000, 0]@6..11#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@13..14#ROOT2024
+              LITERAL Integer 4i64 42:Root[0000, 0]@14..18#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@18..19#ROOT2024
+              LITERAL Float 3.14f32 42:Root[0000, 0]@20..27#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@27..28#ROOT2024
+              LITERAL Str hello bridge 42:Root[0000, 0]@29..43#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@43..44#ROOT2024
+              LITERAL Str suffixedsuffix 42:Root[0000, 0]@45..61#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@61..62#ROOT2024
+              LITERAL StrRaw(2) raw 42:Root[0000, 0]@63..73#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@73..74#ROOT2024
+              LITERAL Char a 42:Root[0000, 0]@75..78#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@78..79#ROOT2024
+              LITERAL Byte b 42:Root[0000, 0]@80..84#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@84..85#ROOT2024
+              LITERAL CStr null 42:Root[0000, 0]@86..93#ROOT2024"#]],
     );
 }
 
@@ -440,33 +442,33 @@
               PUNCH   - [alone] 1
               LITERAL Float 2.7 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              PUNCH   - [alone] 42:2@0..1#ROOT2024
-              LITERAL Integer 1u16 42:2@1..5#ROOT2024
-              PUNCH   , [alone] 42:2@5..6#ROOT2024
-              PUNCH   - [alone] 42:2@7..8#ROOT2024
-              LITERAL Integer 2_u32 42:2@9..14#ROOT2024
-              PUNCH   , [alone] 42:2@14..15#ROOT2024
-              PUNCH   - [alone] 42:2@16..17#ROOT2024
-              LITERAL Float 3.14f32 42:2@17..24#ROOT2024
-              PUNCH   , [alone] 42:2@24..25#ROOT2024
-              PUNCH   - [alone] 42:2@26..27#ROOT2024
-              LITERAL Float 2.7 42:2@28..31#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@0..1#ROOT2024
+              LITERAL Integer 1u16 42:Root[0000, 0]@1..5#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@7..8#ROOT2024
+              LITERAL Integer 2_u32 42:Root[0000, 0]@9..14#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@14..15#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@16..17#ROOT2024
+              LITERAL Float 3.14f32 42:Root[0000, 0]@17..24#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@24..25#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@26..27#ROOT2024
+              LITERAL Float 2.7 42:Root[0000, 0]@28..31#ROOT2024
 
 
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              PUNCH   - [alone] 42:2@0..1#ROOT2024
-              LITERAL Integer 1u16 42:2@1..5#ROOT2024
-              PUNCH   , [alone] 42:2@5..6#ROOT2024
-              PUNCH   - [alone] 42:2@7..8#ROOT2024
-              LITERAL Integer 2_u32 42:2@9..14#ROOT2024
-              PUNCH   , [alone] 42:2@14..15#ROOT2024
-              PUNCH   - [alone] 42:2@16..17#ROOT2024
-              LITERAL Float 3.14f32 42:2@17..24#ROOT2024
-              PUNCH   , [alone] 42:2@24..25#ROOT2024
-              PUNCH   - [alone] 42:2@26..27#ROOT2024
-              LITERAL Float 2.7 42:2@28..31#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@0..1#ROOT2024
+              LITERAL Integer 1u16 42:Root[0000, 0]@1..5#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@7..8#ROOT2024
+              LITERAL Integer 2_u32 42:Root[0000, 0]@9..14#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@14..15#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@16..17#ROOT2024
+              LITERAL Float 3.14f32 42:Root[0000, 0]@17..24#ROOT2024
+              PUNCH   , [alone] 42:Root[0000, 0]@24..25#ROOT2024
+              PUNCH   - [alone] 42:Root[0000, 0]@26..27#ROOT2024
+              LITERAL Float 2.7 42:Root[0000, 0]@28..31#ROOT2024"#]],
     );
 }
 
@@ -496,21 +498,21 @@
                 LITERAL Str #[attr_error(some arguments)] mod m {} 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   mod 42:2@0..3#ROOT2024
-              IDENT   m 42:2@4..5#ROOT2024
-              SUBTREE {} 42:2@6..7#ROOT2024 42:2@7..8#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   mod 42:Root[0000, 0]@0..3#ROOT2024
+              IDENT   m 42:Root[0000, 0]@4..5#ROOT2024
+              SUBTREE {} 42:Root[0000, 0]@6..7#ROOT2024 42:Root[0000, 0]@7..8#ROOT2024
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   some 42:2@0..4#ROOT2024
-              IDENT   arguments 42:2@5..14#ROOT2024
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   some 42:Root[0000, 0]@0..4#ROOT2024
+              IDENT   arguments 42:Root[0000, 0]@5..14#ROOT2024
 
-            SUBTREE $$ 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-              IDENT   compile_error 42:2@0..100#ROOT2024
-              PUNCH   ! [alone] 42:2@0..100#ROOT2024
-              SUBTREE () 42:2@0..100#ROOT2024 42:2@0..100#ROOT2024
-                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#ROOT2024
-              PUNCH   ; [alone] 42:2@0..100#ROOT2024"#]],
+            SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+              IDENT   compile_error 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   ! [alone] 42:Root[0000, 0]@0..100#ROOT2024
+              SUBTREE () 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+                LITERAL Str #[attr_error(some arguments)] mod m {} 42:Root[0000, 0]@0..100#ROOT2024
+              PUNCH   ; [alone] 42:Root[0000, 0]@0..100#ROOT2024"#]],
     );
 }
 
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index a0a45b2..10af566 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -1,7 +1,9 @@
 //! utils used in proc-macro tests
 
 use expect_test::Expect;
-use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContext, TokenId};
+use span::{
+    EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TokenId,
+};
 use tt::TextRange;
 
 use crate::{EnvSnapshot, ProcMacroSrv, dylib, proc_macro_test_dylib_path};
@@ -76,7 +78,7 @@
         range: TextRange::new(0.into(), 150.into()),
         anchor: SpanAnchor {
             file_id: EditionedFileId::current_edition(FileId::from_raw(41)),
-            ast_id: ErasedFileAstId::from_raw(1),
+            ast_id: ROOT_ERASED_FILE_AST_ID,
         },
         ctx: SyntaxContext::root(span::Edition::CURRENT),
     };
@@ -84,7 +86,7 @@
         range: TextRange::new(0.into(), 100.into()),
         anchor: SpanAnchor {
             file_id: EditionedFileId::current_edition(FileId::from_raw(42)),
-            ast_id: ErasedFileAstId::from_raw(2),
+            ast_id: ROOT_ERASED_FILE_AST_ID,
         },
         ctx: SyntaxContext::root(span::Edition::CURRENT),
     };
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 1fb1383..bae891c 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -12,7 +12,7 @@
 [lib]
 
 [dependencies]
-cfg-if = "1.0.0"
+cfg-if = "1.0.1"
 jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true }
 
 [target.'cfg(all(target_os = "linux", not(target_env = "ohos")))'.dependencies]
@@ -22,7 +22,7 @@
 libc.workspace = true
 
 [target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.59", features = [
+windows-sys = { version = "0.60", features = [
     "Win32_System_Threading",
     "Win32_System_ProcessStatus",
 ] }
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index bb02284..1fade7b 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -492,7 +492,7 @@
             is_virtual_workspace &= manifest != ws_manifest_path;
             let pkg = packages.alloc(PackageData {
                 id: id.repr.clone(),
-                name,
+                name: name.to_string(),
                 version,
                 manifest: manifest.clone(),
                 targets: Vec::new(),
@@ -547,10 +547,12 @@
                 .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind)));
             for (dep_node, kind) in dependencies {
                 let &pkg = pkg_by_id.get(&dep_node.pkg).unwrap();
-                let dep = PackageDependency { name: dep_node.name.clone(), pkg, kind };
+                let dep = PackageDependency { name: dep_node.name.to_string(), pkg, kind };
                 packages[source].dependencies.push(dep);
             }
-            packages[source].active_features.extend(node.features);
+            packages[source]
+                .active_features
+                .extend(node.features.into_iter().map(|it| it.to_string()));
         }
 
         CargoWorkspace {
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index d4055d9..ebd86e3 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -339,7 +339,7 @@
                     Some(_) => {
                         tracing::warn!("unknown rustc-std-workspace-* crate: {}", package.name)
                     }
-                    None => match &*package.name {
+                    None => match &**package.name {
                         "core" => real_core = Some(package.id.clone()),
                         "alloc" => real_alloc = Some(package.id.clone()),
                         "std" => real_std = Some(package.id.clone()),
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index b59d068..5e63521 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -29,7 +29,7 @@
 itertools.workspace = true
 scip = "0.5.2"
 lsp-types = { version = "=0.95.0", features = ["proposed"] }
-parking_lot = "0.12.3"
+parking_lot = "0.12.4"
 xflags = "0.3.2"
 oorandom = "11.1.5"
 rayon.workspace = true
@@ -37,19 +37,19 @@
 serde_json = { workspace = true, features = ["preserve_order"] }
 serde.workspace = true
 serde_derive.workspace = true
-tenthash = "1.0.0"
-num_cpus = "1.16.0"
-mimalloc = { version = "0.1.44", default-features = false, optional = true }
+tenthash = "1.1.0"
+num_cpus = "1.17.0"
+mimalloc = { version = "0.1.46", default-features = false, optional = true }
 lsp-server.workspace = true
 tracing.workspace = true
 tracing-subscriber.workspace = true
 tracing-tree.workspace = true
 triomphe.workspace = true
-toml = "0.8.20"
+toml = "0.8.23"
 nohash-hasher.workspace = true
 walkdir = "2.5.0"
 semver.workspace = true
-memchr = "2.7.4"
+memchr = "2.7.5"
 cargo_metadata.workspace = true
 process-wrap.workspace = true
 
@@ -75,7 +75,7 @@
 paths.workspace = true
 
 [target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.59", features = [
+windows-sys = { version = "0.60", features = [
   "Win32_System_Diagnostics_Debug",
   "Win32_System_Threading",
 ] }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 5cbea9c..762b63f 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -760,6 +760,8 @@
         /// though Cargo might be the eventual consumer.
         vfs_extraIncludes: Vec<String> = vec![],
 
+        /// Exclude imports from symbol search.
+        workspace_symbol_search_excludeImports: bool = false,
         /// Workspace symbol search kind.
         workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
         /// Limits the number of items returned from a workspace symbol search (Defaults to 128).
@@ -1352,6 +1354,8 @@
 /// Configuration for workspace symbol search requests.
 #[derive(Debug, Clone)]
 pub struct WorkspaceSymbolConfig {
+    /// Should imports be excluded.
+    pub search_exclude_imports: bool,
     /// In what scope should the symbol be searched in.
     pub search_scope: WorkspaceSymbolSearchScope,
     /// What kind of symbol is being searched for.
@@ -2280,6 +2284,7 @@
 
     pub fn workspace_symbol(&self, source_root: Option<SourceRootId>) -> WorkspaceSymbolConfig {
         WorkspaceSymbolConfig {
+            search_exclude_imports: *self.workspace_symbol_search_excludeImports(source_root),
             search_scope: match self.workspace_symbol_search_scope(source_root) {
                 WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace,
                 WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => {
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 6d46ce6..afd9eff 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -658,6 +658,9 @@
         if libs {
             q.libs();
         }
+        if config.search_exclude_imports {
+            q.exclude_imports();
+        }
         q
     };
     let mut res = exec_query(&snap, query, config.search_limit)?;
diff --git a/crates/span/Cargo.toml b/crates/span/Cargo.toml
index b3b401c..966962b 100644
--- a/crates/span/Cargo.toml
+++ b/crates/span/Cargo.toml
@@ -22,6 +22,9 @@
 syntax.workspace = true
 stdx.workspace = true
 
+[dev-dependencies]
+syntax.workspace = true
+
 [features]
 default = ["salsa"]
 
diff --git a/crates/span/src/ast_id.rs b/crates/span/src/ast_id.rs
index 228fba1..8e95971 100644
--- a/crates/span/src/ast_id.rs
+++ b/crates/span/src/ast_id.rs
@@ -4,137 +4,535 @@
 //! Specifically, it enumerates all items in a file and uses position of a an
 //! item as an ID. That way, id's don't change unless the set of items itself
 //! changes.
+//!
+//! These IDs are tricky. If one of them invalidates, its interned ID invalidates,
+//! and this can cause *a lot* to be recomputed. For example, if you invalidate the ID
+//! of a struct, and that struct has an impl (any impl!) this will cause the `Self`
+//! type of the impl to invalidate, which will cause the all impls queries to be
+//! invalidated, which will cause every trait solve query in this crate *and* all
+//! transitive reverse dependencies to be invalidated, which is pretty much the worst
+//! thing that can happen incrementality wise.
+//!
+//! So we want these IDs to stay as stable as possible. For top-level items, we store
+//! their kind and name, which should be unique, but since they can still not be, we
+//! also store an index disambiguator. For nested items, we also store the ID of their
+//! parent. For macro calls, we store the macro name and an index. There aren't usually
+//! a lot of macro calls in item position, and invalidation in bodies is not much of
+//! a problem, so this should be enough.
 
 use std::{
     any::type_name,
     fmt,
-    hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
+    hash::{BuildHasher, Hash, Hasher},
     marker::PhantomData,
 };
 
 use la_arena::{Arena, Idx, RawIdx};
-use rustc_hash::FxHasher;
-use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ast};
+use rustc_hash::{FxBuildHasher, FxHashMap};
+use syntax::{
+    AstNode, AstPtr, SyntaxKind, SyntaxNode, SyntaxNodePtr,
+    ast::{self, HasName},
+    match_ast,
+};
 
-/// See crates\hir-expand\src\ast_id_map.rs
+// The first index is always the root node's AstId
+/// The root ast id always points to the encompassing file, using this in spans is discouraged as
+/// any range relative to it will be effectively absolute, ruining the entire point of anchored
+/// relative text ranges.
+pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
+    ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::Root as u32));
+
+/// ErasedFileAstId used as the span for syntax node fixups. Any Span containing this file id is to be
+/// considered fake.
+pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
+    ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::Fixup as u32));
+
 /// This is a type erased FileAstId.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ErasedFileAstId(u32);
 
-impl ErasedFileAstId {
-    pub const fn into_raw(self) -> u32 {
-        self.0
-    }
-    pub const fn from_raw(u32: u32) -> Self {
-        Self(u32)
-    }
-}
-
-impl fmt::Display for ErasedFileAstId {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
 impl fmt::Debug for ErasedFileAstId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
+        let kind = self.kind();
+        macro_rules! kind {
+            ($($kind:ident),* $(,)?) => {
+                if false {
+                    // Ensure we covered all variants.
+                    match ErasedFileAstIdKind::Root {
+                        $( ErasedFileAstIdKind::$kind => {} )*
+                    }
+                    unreachable!()
+                }
+                $( else if kind == ErasedFileAstIdKind::$kind as u32 {
+                    stringify!($kind)
+                } )*
+                else {
+                    "Unknown"
+                }
+            };
+        }
+        let kind = kind!(
+            Root,
+            Enum,
+            Struct,
+            Union,
+            ExternCrate,
+            MacroDef,
+            MacroRules,
+            Module,
+            Static,
+            Trait,
+            TraitAlias,
+            Variant,
+            Const,
+            Fn,
+            MacroCall,
+            TypeAlias,
+            ExternBlock,
+            Use,
+            Impl,
+            BlockExpr,
+            Fixup,
+        );
+        if f.alternate() {
+            write!(f, "{kind}[{:04X}, {}]", self.hash_value(), self.index())
+        } else {
+            f.debug_struct("ErasedFileAstId")
+                .field("kind", &format_args!("{kind}"))
+                .field("index", &self.index())
+                .field("hash", &format_args!("{:04X}", self.hash_value()))
+                .finish()
+        }
     }
 }
 
-/// `AstId` points to an AST node in a specific file.
-pub struct FileAstId<N: AstIdNode> {
-    raw: ErasedFileAstId,
-    covariant: PhantomData<fn() -> N>,
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+enum ErasedFileAstIdKind {
+    /// This needs to not change because it's depended upon by the proc macro server.
+    Fixup,
+    // The following are associated with `ErasedHasNameFileAstId`.
+    Enum,
+    Struct,
+    Union,
+    ExternCrate,
+    MacroDef,
+    MacroRules,
+    Module,
+    Static,
+    Trait,
+    TraitAlias,
+    // Until here associated with `ErasedHasNameFileAstId`.
+    // The following are associated with `ErasedAssocItemFileAstId`.
+    Variant,
+    Const,
+    Fn,
+    MacroCall,
+    TypeAlias,
+    // Until here associated with `ErasedAssocItemFileAstId`.
+    // Extern blocks don't really have any identifying property unfortunately.
+    ExternBlock,
+    // FIXME: If we store the final `UseTree` instead of the top-level `Use`, we can store its name,
+    // and be way more granular for incrementality, at the expense of increased memory usage.
+    // Use IDs aren't used a lot. The main thing that stores them is the def map. So everything that
+    // uses the def map will be invalidated. That includes infers, and so is pretty bad, but our
+    // def map incrementality story is pretty bad anyway and needs to be improved (see
+    // https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/.60infer.60.20queries.20and.20splitting.20.60DefMap.60).
+    // So I left this as-is for now, as the def map improvement should also mitigate this.
+    Use,
+    /// Associated with [`ImplFileAstId`].
+    Impl,
+    /// Associated with [`BlockExprFileAstId`].
+    BlockExpr,
+    /// Keep this last.
+    Root,
 }
 
-impl<N: AstIdNode> Clone for FileAstId<N> {
+// First hash, then index, then kind.
+const HASH_BITS: u32 = 16;
+const INDEX_BITS: u32 = 11;
+const KIND_BITS: u32 = 5;
+const _: () = assert!(ErasedFileAstIdKind::Fixup as u32 <= ((1 << KIND_BITS) - 1));
+const _: () = assert!(HASH_BITS + INDEX_BITS + KIND_BITS == u32::BITS);
+
+#[inline]
+const fn u16_hash(hash: u64) -> u16 {
+    // We do basically the same as `FxHasher`. We don't use rustc-hash and truncate because the
+    // higher bits have more entropy, but unlike rustc-hash we don't rotate because it rotates
+    // for hashmaps that just use the low bits, but we compare all bits.
+    const K: u16 = 0xecc5;
+    let (part1, part2, part3, part4) =
+        (hash as u16, (hash >> 16) as u16, (hash >> 32) as u16, (hash >> 48) as u16);
+    part1
+        .wrapping_add(part2)
+        .wrapping_mul(K)
+        .wrapping_add(part3)
+        .wrapping_mul(K)
+        .wrapping_add(part4)
+        .wrapping_mul(K)
+}
+
+#[inline]
+const fn pack_hash_index_and_kind(hash: u16, index: u32, kind: u32) -> u32 {
+    (hash as u32) | (index << HASH_BITS) | (kind << (HASH_BITS + INDEX_BITS))
+}
+
+impl ErasedFileAstId {
+    #[inline]
+    fn hash_value(self) -> u16 {
+        self.0 as u16
+    }
+
+    #[inline]
+    fn index(self) -> u32 {
+        (self.0 << KIND_BITS) >> (HASH_BITS + KIND_BITS)
+    }
+
+    #[inline]
+    fn kind(self) -> u32 {
+        self.0 >> (HASH_BITS + INDEX_BITS)
+    }
+
+    fn ast_id_for(
+        node: &SyntaxNode,
+        index_map: &mut ErasedAstIdNextIndexMap,
+        parent: Option<&ErasedFileAstId>,
+    ) -> Option<ErasedFileAstId> {
+        // Blocks are deliberately not here - we only want to allocate a block if it contains items.
+        has_name_ast_id(node, index_map)
+            .or_else(|| assoc_item_ast_id(node, index_map, parent))
+            .or_else(|| extern_block_ast_id(node, index_map))
+            .or_else(|| use_ast_id(node, index_map))
+            .or_else(|| impl_ast_id(node, index_map))
+    }
+
+    fn should_alloc(node: &SyntaxNode) -> bool {
+        should_alloc_has_name(node)
+            || should_alloc_assoc_item(node)
+            || ast::ExternBlock::can_cast(node.kind())
+            || ast::Use::can_cast(node.kind())
+            || ast::Impl::can_cast(node.kind())
+    }
+
+    #[inline]
+    pub fn into_raw(self) -> u32 {
+        self.0
+    }
+
+    #[inline]
+    pub const fn from_raw(v: u32) -> Self {
+        Self(v)
+    }
+}
+
+pub trait AstIdNode: AstNode {}
+
+/// `AstId` points to an AST node in a specific file.
+pub struct FileAstId<N> {
+    raw: ErasedFileAstId,
+    _marker: PhantomData<fn() -> N>,
+}
+
+/// Traits are manually implemented because `derive` adds redundant bounds.
+impl<N> Clone for FileAstId<N> {
+    #[inline]
     fn clone(&self) -> FileAstId<N> {
         *self
     }
 }
-impl<N: AstIdNode> Copy for FileAstId<N> {}
+impl<N> Copy for FileAstId<N> {}
 
-impl<N: AstIdNode> PartialEq for FileAstId<N> {
+impl<N> PartialEq for FileAstId<N> {
     fn eq(&self, other: &Self) -> bool {
         self.raw == other.raw
     }
 }
-impl<N: AstIdNode> Eq for FileAstId<N> {}
-impl<N: AstIdNode> Hash for FileAstId<N> {
+impl<N> Eq for FileAstId<N> {}
+impl<N> Hash for FileAstId<N> {
     fn hash<H: Hasher>(&self, hasher: &mut H) {
         self.raw.hash(hasher);
     }
 }
 
-impl<N: AstIdNode> fmt::Debug for FileAstId<N> {
+impl<N> fmt::Debug for FileAstId<N> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw)
+        write!(f, "FileAstId::<{}>({:?})", type_name::<N>(), self.raw)
     }
 }
 
-impl<N: AstIdNode> FileAstId<N> {
+impl<N> FileAstId<N> {
     // Can't make this a From implementation because of coherence
+    #[inline]
     pub fn upcast<M: AstIdNode>(self) -> FileAstId<M>
     where
         N: Into<M>,
     {
-        FileAstId { raw: self.raw, covariant: PhantomData }
+        FileAstId { raw: self.raw, _marker: PhantomData }
     }
 
+    #[inline]
     pub fn erase(self) -> ErasedFileAstId {
         self.raw
     }
 }
 
-pub trait AstIdNode: AstNode {}
-macro_rules! register_ast_id_node {
+#[derive(Hash)]
+struct ErasedHasNameFileAstId<'a> {
+    kind: SyntaxKind,
+    name: &'a str,
+}
+
+/// This holds the ast ID for variants too (they're a kind of assoc item).
+#[derive(Hash)]
+struct ErasedAssocItemFileAstId<'a> {
+    /// Subtle: items in `extern` blocks **do not** store the ID of the extern block here.
+    /// Instead this is left empty. The reason is that `ExternBlockFileAstId` is pretty unstable
+    /// (it contains only an index), and extern blocks don't introduce a new scope, so storing
+    /// the extern block ID will do more harm to incrementality than help.
+    parent: Option<ErasedFileAstId>,
+    properties: ErasedHasNameFileAstId<'a>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct ImplFileAstId<'a> {
+    /// This can be `None` if the `Self` type is not a named type, or if it is inside a macro call.
+    self_ty_name: Option<&'a str>,
+    /// This can be `None` if this is an inherent impl, or if the trait name is inside a macro call.
+    trait_name: Option<&'a str>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct BlockExprFileAstId {
+    parent: Option<ErasedFileAstId>,
+}
+
+impl AstIdNode for ast::ExternBlock {}
+
+fn extern_block_ast_id(
+    node: &SyntaxNode,
+    index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+    if ast::ExternBlock::can_cast(node.kind()) {
+        Some(index_map.new_id(ErasedFileAstIdKind::ExternBlock, ()))
+    } else {
+        None
+    }
+}
+
+impl AstIdNode for ast::Use {}
+
+fn use_ast_id(
+    node: &SyntaxNode,
+    index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+    if ast::Use::can_cast(node.kind()) {
+        Some(index_map.new_id(ErasedFileAstIdKind::Use, ()))
+    } else {
+        None
+    }
+}
+
+impl AstIdNode for ast::Impl {}
+
+fn impl_ast_id(
+    node: &SyntaxNode,
+    index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+    if let Some(node) = ast::Impl::cast(node.clone()) {
+        let type_as_name = |ty: Option<ast::Type>| match ty? {
+            ast::Type::PathType(it) => Some(it.path()?.segment()?.name_ref()?),
+            _ => None,
+        };
+        let self_ty_name = type_as_name(node.self_ty());
+        let trait_name = type_as_name(node.trait_());
+        let data = ImplFileAstId {
+            self_ty_name: self_ty_name.as_ref().map(|it| it.text_non_mutable()),
+            trait_name: trait_name.as_ref().map(|it| it.text_non_mutable()),
+        };
+        Some(index_map.new_id(ErasedFileAstIdKind::Impl, data))
+    } else {
+        None
+    }
+}
+
+// Blocks aren't `AstIdNode`s deliberately, because unlike other nodes, not all blocks get their own
+// ast id, only if they have items. To account for that we have a different, fallible, API for blocks.
+// impl !AstIdNode for ast::BlockExpr {}
+
+fn block_expr_ast_id(
+    node: &SyntaxNode,
+    index_map: &mut ErasedAstIdNextIndexMap,
+    parent: Option<&ErasedFileAstId>,
+) -> Option<ErasedFileAstId> {
+    if ast::BlockExpr::can_cast(node.kind()) {
+        Some(
+            index_map.new_id(
+                ErasedFileAstIdKind::BlockExpr,
+                BlockExprFileAstId { parent: parent.copied() },
+            ),
+        )
+    } else {
+        None
+    }
+}
+
+#[derive(Default)]
+struct ErasedAstIdNextIndexMap(FxHashMap<(ErasedFileAstIdKind, u16), u32>);
+
+impl ErasedAstIdNextIndexMap {
+    #[inline]
+    fn new_id(&mut self, kind: ErasedFileAstIdKind, data: impl Hash) -> ErasedFileAstId {
+        let hash = FxBuildHasher.hash_one(&data);
+        let initial_hash = u16_hash(hash);
+        // Even though 2^INDEX_BITS=2048 items with the same hash seems like a lot,
+        // it could happen with macro calls or `use`s in macro-generated files. So we want
+        // to handle it gracefully. We just increment the hash.
+        let mut hash = initial_hash;
+        let index = loop {
+            match self.0.entry((kind, hash)) {
+                std::collections::hash_map::Entry::Occupied(mut entry) => {
+                    let i = entry.get_mut();
+                    if *i < ((1 << INDEX_BITS) - 1) {
+                        *i += 1;
+                        break *i;
+                    }
+                }
+                std::collections::hash_map::Entry::Vacant(entry) => {
+                    entry.insert(0);
+                    break 0;
+                }
+            }
+            hash = hash.wrapping_add(1);
+            if hash == initial_hash {
+                // That's 2^27=134,217,728 items!
+                panic!("you have way too many items in the same file!");
+            }
+        };
+        let kind = kind as u32;
+        ErasedFileAstId(pack_hash_index_and_kind(hash, index, kind))
+    }
+}
+
+macro_rules! register_enum_ast_id {
     (impl AstIdNode for $($ident:ident),+ ) => {
         $(
             impl AstIdNode for ast::$ident {}
         )+
-        fn should_alloc_id(kind: syntax::SyntaxKind) -> bool {
-            $(
-                ast::$ident::can_cast(kind)
-            )||+
+    };
+}
+register_enum_ast_id! {
+    impl AstIdNode for
+    Item, AnyHasGenericParams, Adt, Macro,
+    AssocItem
+}
+
+macro_rules! register_has_name_ast_id {
+    (impl AstIdNode for $($ident:ident = $name_method:ident),+ ) => {
+        $(
+            impl AstIdNode for ast::$ident {}
+        )+
+
+        fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
+            let kind = node.kind();
+            match_ast! {
+                match node {
+                    $(
+                        ast::$ident(node) => {
+                            let name = node.$name_method();
+                            let name = name.as_ref().map_or("", |it| it.text_non_mutable());
+                            let result = ErasedHasNameFileAstId {
+                                kind,
+                                name,
+                            };
+                            Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
+                        },
+                    )*
+                    _ => None,
+                }
+            }
+        }
+
+        fn should_alloc_has_name(node: &SyntaxNode) -> bool {
+            let kind = node.kind();
+            false $( || ast::$ident::can_cast(kind) )*
         }
     };
 }
-register_ast_id_node! {
+register_has_name_ast_id! {
     impl AstIdNode for
-    Item, AnyHasGenericParams,
-        Adt,
-            Enum,
-                Variant,
-            Struct,
-            Union,
-        AssocItem,
-            Const,
-            Fn,
-            MacroCall,
-            TypeAlias,
-        ExternBlock,
-        ExternCrate,
-        Impl,
-        Macro,
-            MacroDef,
-            MacroRules,
-        Module,
-        Static,
-        Trait,
-        TraitAlias,
-        Use,
-    BlockExpr, ConstArg
+        Enum = name,
+        Struct = name,
+        Union = name,
+        ExternCrate = name_ref,
+        MacroDef = name,
+        MacroRules = name,
+        Module = name,
+        Static = name,
+        Trait = name,
+        TraitAlias = name
+}
+
+macro_rules! register_assoc_item_ast_id {
+    (impl AstIdNode for $($ident:ident = $name_callback:expr),+ ) => {
+        $(
+            impl AstIdNode for ast::$ident {}
+        )+
+
+        fn assoc_item_ast_id(
+            node: &SyntaxNode,
+            index_map: &mut ErasedAstIdNextIndexMap,
+            parent: Option<&ErasedFileAstId>,
+        ) -> Option<ErasedFileAstId> {
+            let kind = node.kind();
+            match_ast! {
+                match node {
+                    $(
+                        ast::$ident(node) => {
+                            let name = $name_callback(node);
+                            let name = name.as_ref().map_or("", |it| it.text_non_mutable());
+                            let properties = ErasedHasNameFileAstId {
+                                kind,
+                                name,
+                            };
+                            let result = ErasedAssocItemFileAstId {
+                                parent: parent.copied(),
+                                properties,
+                            };
+                            Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
+                        },
+                    )*
+                    _ => None,
+                }
+            }
+        }
+
+        fn should_alloc_assoc_item(node: &SyntaxNode) -> bool {
+            let kind = node.kind();
+            false $( || ast::$ident::can_cast(kind) )*
+        }
+    };
+}
+register_assoc_item_ast_id! {
+    impl AstIdNode for
+    Variant = |it: ast::Variant| it.name(),
+    Const = |it: ast::Const| it.name(),
+    Fn = |it: ast::Fn| it.name(),
+    MacroCall = |it: ast::MacroCall| it.path().and_then(|path| path.segment()?.name_ref()),
+    TypeAlias = |it: ast::TypeAlias| it.name()
 }
 
 /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
 #[derive(Default)]
 pub struct AstIdMap {
-    /// Maps stable id to unstable ptr.
-    arena: Arena<SyntaxNodePtr>,
-    /// Reverse: map ptr to id.
-    map: hashbrown::HashTable<Idx<SyntaxNodePtr>>,
+    /// An arena of the ptrs and their associated ID.
+    arena: Arena<(SyntaxNodePtr, ErasedFileAstId)>,
+    /// Map ptr to id.
+    ptr_map: hashbrown::HashTable<ArenaId>,
+    /// Map id to ptr.
+    id_map: hashbrown::HashTable<ArenaId>,
 }
 
+type ArenaId = Idx<(SyntaxNodePtr, ErasedFileAstId)>;
+
 impl fmt::Debug for AstIdMap {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("AstIdMap").field("arena", &self.arena).finish()
@@ -148,31 +546,116 @@
 }
 impl Eq for AstIdMap {}
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ContainsItems {
+    Yes,
+    No,
+}
+
 impl AstIdMap {
     pub fn from_source(node: &SyntaxNode) -> AstIdMap {
         assert!(node.parent().is_none());
         let mut res = AstIdMap::default();
+        let mut index_map = ErasedAstIdNextIndexMap::default();
 
-        // make sure to allocate the root node
-        if !should_alloc_id(node.kind()) {
-            res.alloc(node);
-        }
+        // Ensure we allocate the root.
+        res.arena.alloc((SyntaxNodePtr::new(node), ROOT_ERASED_FILE_AST_ID));
+
         // By walking the tree in breadth-first order we make sure that parents
         // get lower ids then children. That is, adding a new child does not
         // change parent's id. This means that, say, adding a new function to a
         // trait does not change ids of top-level items, which helps caching.
-        bdfs(node, |it| {
-            if should_alloc_id(it.kind()) {
-                res.alloc(&it);
-                TreeOrder::BreadthFirst
-            } else {
-                TreeOrder::DepthFirst
+
+        // This contains the stack of the `BlockExpr`s we are under. We do this
+        // so we only allocate `BlockExpr`s if they contain items.
+        // The general idea is: when we enter a block we push `(block, false)` here.
+        // Items inside the block are attributed to the block's container, not the block.
+        // For the first item we find inside a block, we make this `(block, true)`
+        // and create an ast id for the block. When exiting the block we pop it,
+        // whether or not we created an ast id for it.
+        // It may seem that with this setup we will generate an ID for blocks that
+        // have no items directly but have items inside other items inside them.
+        // This is true, but it doesn't matter, because such blocks can't exist.
+        // After all, the block will then contain the *outer* item, so we allocate
+        // an ID for it anyway.
+        let mut blocks = Vec::new();
+        let mut curr_layer = vec![(node.clone(), None)];
+        let mut next_layer = vec![];
+        while !curr_layer.is_empty() {
+            curr_layer.drain(..).for_each(|(node, parent_idx)| {
+                let mut preorder = node.preorder();
+                while let Some(event) = preorder.next() {
+                    match event {
+                        syntax::WalkEvent::Enter(node) => {
+                            if ast::BlockExpr::can_cast(node.kind()) {
+                                blocks.push((node, ContainsItems::No));
+                            } else if ErasedFileAstId::should_alloc(&node) {
+                                // Allocate blocks on-demand, only if they have items.
+                                // We don't associate items with blocks, only with items, since block IDs can be quite unstable.
+                                // FIXME: Is this the correct thing to do? Macro calls might actually be more incremental if
+                                // associated with blocks (not sure). Either way it's not a big deal.
+                                if let Some((
+                                    last_block_node,
+                                    already_allocated @ ContainsItems::No,
+                                )) = blocks.last_mut()
+                                {
+                                    let block_ast_id = block_expr_ast_id(
+                                        last_block_node,
+                                        &mut index_map,
+                                        parent_of(parent_idx, &res),
+                                    )
+                                    .expect("not a BlockExpr");
+                                    res.arena
+                                        .alloc((SyntaxNodePtr::new(last_block_node), block_ast_id));
+                                    *already_allocated = ContainsItems::Yes;
+                                }
+
+                                let parent = parent_of(parent_idx, &res);
+                                let ast_id =
+                                    ErasedFileAstId::ast_id_for(&node, &mut index_map, parent)
+                                        .expect("this node should have an ast id");
+                                let idx = res.arena.alloc((SyntaxNodePtr::new(&node), ast_id));
+
+                                next_layer.extend(node.children().map(|child| (child, Some(idx))));
+                                preorder.skip_subtree();
+                            }
+                        }
+                        syntax::WalkEvent::Leave(node) => {
+                            if ast::BlockExpr::can_cast(node.kind()) {
+                                assert_eq!(
+                                    blocks.pop().map(|it| it.0),
+                                    Some(node),
+                                    "left a BlockExpr we never entered"
+                                );
+                            }
+                        }
+                    }
+                }
+            });
+            std::mem::swap(&mut curr_layer, &mut next_layer);
+            assert!(blocks.is_empty(), "didn't leave all BlockExprs");
+        }
+
+        res.ptr_map = hashbrown::HashTable::with_capacity(res.arena.len());
+        res.id_map = hashbrown::HashTable::with_capacity(res.arena.len());
+        for (idx, (ptr, ast_id)) in res.arena.iter() {
+            let ptr_hash = hash_ptr(ptr);
+            let ast_id_hash = hash_ast_id(ast_id);
+            match res.ptr_map.entry(
+                ptr_hash,
+                |idx2| *idx2 == idx,
+                |&idx| hash_ptr(&res.arena[idx].0),
+            ) {
+                hashbrown::hash_table::Entry::Occupied(_) => unreachable!(),
+                hashbrown::hash_table::Entry::Vacant(entry) => {
+                    entry.insert(idx);
+                }
             }
-        });
-        res.map = hashbrown::HashTable::with_capacity(res.arena.len());
-        for (idx, ptr) in res.arena.iter() {
-            let hash = hash_ptr(ptr);
-            match res.map.entry(hash, |&idx2| idx2 == idx, |&idx| hash_ptr(&res.arena[idx])) {
+            match res.id_map.entry(
+                ast_id_hash,
+                |idx2| *idx2 == idx,
+                |&idx| hash_ast_id(&res.arena[idx].1),
+            ) {
                 hashbrown::hash_table::Entry::Occupied(_) => unreachable!(),
                 hashbrown::hash_table::Entry::Vacant(entry) => {
                     entry.insert(idx);
@@ -180,98 +663,235 @@
             }
         }
         res.arena.shrink_to_fit();
-        res
+        return res;
+
+        fn parent_of(parent_idx: Option<ArenaId>, res: &AstIdMap) -> Option<&ErasedFileAstId> {
+            let mut parent = parent_idx.map(|parent_idx| &res.arena[parent_idx].1);
+            if parent.is_some_and(|parent| parent.kind() == ErasedFileAstIdKind::ExternBlock as u32)
+            {
+                // See the comment on `ErasedAssocItemFileAstId` for why is this.
+                // FIXME: Technically there could be an extern block inside another item, e.g.:
+                // ```
+                // fn foo() {
+                //     extern "C" {
+                //         fn bar();
+                //     }
+                // }
+                // ```
+                // Here we want to make `foo()` the parent of `bar()`, but we make it `None`.
+                // Shouldn't be a big deal though.
+                parent = None;
+            }
+            parent
+        }
     }
 
     /// The [`AstId`] of the root node
     pub fn root(&self) -> SyntaxNodePtr {
-        self.arena[Idx::from_raw(RawIdx::from_u32(0))]
+        self.arena[Idx::from_raw(RawIdx::from_u32(0))].0
     }
 
     pub fn ast_id<N: AstIdNode>(&self, item: &N) -> FileAstId<N> {
-        let raw = self.erased_ast_id(item.syntax());
-        FileAstId { raw, covariant: PhantomData }
+        self.ast_id_for_ptr(AstPtr::new(item))
+    }
+
+    /// Blocks may not be allocated (if they have no items), so they have a different API.
+    pub fn ast_id_for_block(&self, block: &ast::BlockExpr) -> Option<FileAstId<ast::BlockExpr>> {
+        self.ast_id_for_ptr_for_block(AstPtr::new(block))
     }
 
     pub fn ast_id_for_ptr<N: AstIdNode>(&self, ptr: AstPtr<N>) -> FileAstId<N> {
         let ptr = ptr.syntax_node_ptr();
-        let hash = hash_ptr(&ptr);
-        match self.map.find(hash, |&idx| self.arena[idx] == ptr) {
-            Some(&raw) => FileAstId {
-                raw: ErasedFileAstId(raw.into_raw().into_u32()),
-                covariant: PhantomData,
-            },
-            None => panic!(
-                "Can't find {:?} in AstIdMap:\n{:?}",
-                ptr,
-                self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
-            ),
-        }
+        FileAstId { raw: self.erased_ast_id(ptr), _marker: PhantomData }
     }
 
-    pub fn get<N: AstIdNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
-        AstPtr::try_from_raw(self.arena[Idx::from_raw(RawIdx::from_u32(id.raw.into_raw()))])
-            .unwrap()
+    /// Blocks may not be allocated (if they have no items), so they have a different API.
+    pub fn ast_id_for_ptr_for_block(
+        &self,
+        ptr: AstPtr<ast::BlockExpr>,
+    ) -> Option<FileAstId<ast::BlockExpr>> {
+        let ptr = ptr.syntax_node_ptr();
+        self.try_erased_ast_id(ptr).map(|raw| FileAstId { raw, _marker: PhantomData })
+    }
+
+    fn erased_ast_id(&self, ptr: SyntaxNodePtr) -> ErasedFileAstId {
+        self.try_erased_ast_id(ptr).unwrap_or_else(|| {
+            panic!(
+                "Can't find SyntaxNodePtr {:?} in AstIdMap:\n{:?}",
+                ptr,
+                self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
+            )
+        })
+    }
+
+    fn try_erased_ast_id(&self, ptr: SyntaxNodePtr) -> Option<ErasedFileAstId> {
+        let hash = hash_ptr(&ptr);
+        let idx = *self.ptr_map.find(hash, |&idx| self.arena[idx].0 == ptr)?;
+        Some(self.arena[idx].1)
+    }
+
+    // Don't bound on `AstIdNode` here, because `BlockExpr`s are also valid here (`ast::BlockExpr`
+    // doesn't always have a matching `FileAstId`, but a `FileAstId<ast::BlockExpr>` always has
+    // a matching node).
+    pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
+        let ptr = self.get_erased(id.raw);
+        AstPtr::try_from_raw(ptr)
+            .unwrap_or_else(|| panic!("AstIdMap node mismatch with node `{ptr:?}`"))
     }
 
     pub fn get_erased(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
-        self.arena[Idx::from_raw(RawIdx::from_u32(id.into_raw()))]
-    }
-
-    fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
-        let ptr = SyntaxNodePtr::new(item);
-        let hash = hash_ptr(&ptr);
-        match self.map.find(hash, |&idx| self.arena[idx] == ptr) {
-            Some(&idx) => ErasedFileAstId(idx.into_raw().into_u32()),
+        let hash = hash_ast_id(&id);
+        match self.id_map.find(hash, |&idx| self.arena[idx].1 == id) {
+            Some(&idx) => self.arena[idx].0,
             None => panic!(
-                "Can't find {:?} in AstIdMap:\n{:?}\n source text: {}",
-                item,
+                "Can't find ast id {:?} in AstIdMap:\n{:?}",
+                id,
                 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
-                item
             ),
         }
     }
-
-    fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
-        ErasedFileAstId(self.arena.alloc(SyntaxNodePtr::new(item)).into_raw().into_u32())
-    }
 }
 
+#[inline]
 fn hash_ptr(ptr: &SyntaxNodePtr) -> u64 {
-    BuildHasherDefault::<FxHasher>::default().hash_one(ptr)
+    FxBuildHasher.hash_one(ptr)
 }
 
-#[derive(Copy, Clone, PartialEq, Eq)]
-enum TreeOrder {
-    BreadthFirst,
-    DepthFirst,
+#[inline]
+fn hash_ast_id(ptr: &ErasedFileAstId) -> u64 {
+    FxBuildHasher.hash_one(ptr)
 }
 
-/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
-/// order? It is a mix of breadth-first and depth first orders. Nodes for which
-/// `f` returns [`TreeOrder::BreadthFirst`] are visited breadth-first, all the other nodes are explored
-/// [`TreeOrder::DepthFirst`].
-///
-/// In other words, the size of the bfs queue is bound by the number of "true"
-/// nodes.
-fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> TreeOrder) {
-    let mut curr_layer = vec![node.clone()];
-    let mut next_layer = vec![];
-    while !curr_layer.is_empty() {
-        curr_layer.drain(..).for_each(|node| {
-            let mut preorder = node.preorder();
-            while let Some(event) = preorder.next() {
-                match event {
-                    syntax::WalkEvent::Enter(node) => {
-                        if f(node.clone()) == TreeOrder::BreadthFirst {
-                            next_layer.extend(node.children());
-                            preorder.skip_subtree();
-                        }
-                    }
-                    syntax::WalkEvent::Leave(_) => {}
-                }
+#[cfg(test)]
+mod tests {
+    use syntax::{AstNode, Edition, SourceFile, SyntaxKind, SyntaxNodePtr, WalkEvent, ast};
+
+    use crate::AstIdMap;
+
+    #[test]
+    fn check_all_nodes() {
+        let syntax = SourceFile::parse(
+            r#"
+extern crate foo;
+fn foo() {
+    union U {}
+}
+struct S;
+macro_rules! m {}
+macro m2() {}
+trait Trait {}
+impl Trait for S {}
+impl S {}
+impl m!() {}
+impl m2!() for m!() {}
+type T = i32;
+enum E {
+    V1(),
+    V2 {},
+    V3,
+}
+struct S; // duplicate
+extern "C" {
+    static S: i32;
+}
+static mut S: i32 = 0;
+const FOO: i32 = 0;
+        "#,
+            Edition::CURRENT,
+        )
+        .syntax_node();
+        let ast_id_map = AstIdMap::from_source(&syntax);
+        for node in syntax.preorder() {
+            let WalkEvent::Enter(node) = node else { continue };
+            if !matches!(
+                node.kind(),
+                SyntaxKind::EXTERN_CRATE
+                    | SyntaxKind::FN
+                    | SyntaxKind::UNION
+                    | SyntaxKind::STRUCT
+                    | SyntaxKind::MACRO_RULES
+                    | SyntaxKind::MACRO_DEF
+                    | SyntaxKind::MACRO_CALL
+                    | SyntaxKind::TRAIT
+                    | SyntaxKind::IMPL
+                    | SyntaxKind::TYPE_ALIAS
+                    | SyntaxKind::ENUM
+                    | SyntaxKind::VARIANT
+                    | SyntaxKind::EXTERN_BLOCK
+                    | SyntaxKind::STATIC
+                    | SyntaxKind::CONST
+            ) {
+                continue;
             }
-        });
-        std::mem::swap(&mut curr_layer, &mut next_layer);
+            let ptr = SyntaxNodePtr::new(&node);
+            let ast_id = ast_id_map.erased_ast_id(ptr);
+            let turn_back = ast_id_map.get_erased(ast_id);
+            assert_eq!(ptr, turn_back);
+        }
+    }
+
+    #[test]
+    fn different_names_get_different_hashes() {
+        let syntax = SourceFile::parse(
+            r#"
+fn foo() {}
+fn bar() {}
+        "#,
+            Edition::CURRENT,
+        )
+        .syntax_node();
+        let ast_id_map = AstIdMap::from_source(&syntax);
+        let fns = syntax.descendants().filter_map(ast::Fn::cast).collect::<Vec<_>>();
+        let [foo_fn, bar_fn] = fns.as_slice() else {
+            panic!("not exactly 2 functions");
+        };
+        let foo_fn_id = ast_id_map.ast_id(foo_fn);
+        let bar_fn_id = ast_id_map.ast_id(bar_fn);
+        assert_ne!(foo_fn_id.raw.hash_value(), bar_fn_id.raw.hash_value(), "hashes are equal");
+    }
+
+    #[test]
+    fn different_parents_get_different_hashes() {
+        let syntax = SourceFile::parse(
+            r#"
+fn foo() {
+    m!();
+}
+fn bar() {
+    m!();
+}
+        "#,
+            Edition::CURRENT,
+        )
+        .syntax_node();
+        let ast_id_map = AstIdMap::from_source(&syntax);
+        let macro_calls = syntax.descendants().filter_map(ast::MacroCall::cast).collect::<Vec<_>>();
+        let [macro_call_foo, macro_call_bar] = macro_calls.as_slice() else {
+            panic!("not exactly 2 macro calls");
+        };
+        let macro_call_foo_id = ast_id_map.ast_id(macro_call_foo);
+        let macro_call_bar_id = ast_id_map.ast_id(macro_call_bar);
+        assert_ne!(
+            macro_call_foo_id.raw.hash_value(),
+            macro_call_bar_id.raw.hash_value(),
+            "hashes are equal"
+        );
+    }
+
+    #[test]
+    fn blocks_with_no_items_have_no_id() {
+        let syntax = SourceFile::parse(
+            r#"
+fn foo() {
+    let foo = 1;
+    bar(foo);
+}
+        "#,
+            Edition::CURRENT,
+        )
+        .syntax_node();
+        let ast_id_map = AstIdMap::from_source(&syntax);
+        let block = syntax.descendants().find_map(ast::BlockExpr::cast).expect("no block");
+        assert!(ast_id_map.ast_id_for_block(&block).is_none());
     }
 }
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs
index f81648a..b81d08e 100644
--- a/crates/span/src/lib.rs
+++ b/crates/span/src/lib.rs
@@ -6,7 +6,10 @@
 mod map;
 
 pub use self::{
-    ast_id::{AstIdMap, AstIdNode, ErasedFileAstId, FileAstId},
+    ast_id::{
+        AstIdMap, AstIdNode, ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, FileAstId,
+        ROOT_ERASED_FILE_AST_ID,
+    },
     hygiene::{SyntaxContext, Transparency},
     map::{RealSpanMap, SpanMap},
 };
@@ -15,19 +18,6 @@
 pub use text_size::{TextRange, TextSize};
 pub use vfs::FileId;
 
-// The first index is always the root node's AstId
-/// The root ast id always points to the encompassing file, using this in spans is discouraged as
-/// any range relative to it will be effectively absolute, ruining the entire point of anchored
-/// relative text ranges.
-pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(0);
-
-/// FileId used as the span for syntax node fixups. Any Span containing this file id is to be
-/// considered fake.
-pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
-    // we pick the second to last for this in case we ever consider making this a NonMaxU32, this
-    // is required to be stable for the proc-macro-server
-    ErasedFileAstId::from_raw(!0 - 1);
-
 pub type Span = SpanData<SyntaxContext>;
 
 impl Span {
@@ -60,7 +50,7 @@
         if f.alternate() {
             fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
             f.write_char(':')?;
-            fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+            write!(f, "{:#?}", self.anchor.ast_id)?;
             f.write_char('@')?;
             fmt::Debug::fmt(&self.range, f)?;
             f.write_char('#')?;
@@ -85,7 +75,7 @@
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
         f.write_char(':')?;
-        fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+        write!(f, "{:#?}", self.anchor.ast_id)?;
         f.write_char('@')?;
         fmt::Debug::fmt(&self.range, f)?;
         f.write_char('#')?;
@@ -101,7 +91,7 @@
 
 impl fmt::Debug for SpanAnchor {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish()
+        f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id).finish()
     }
 }
 
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs
index cc7a886..f582017 100644
--- a/crates/span/src/map.rs
+++ b/crates/span/src/map.rs
@@ -169,7 +169,7 @@
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         writeln!(f, "RealSpanMap({:?}):", self.file_id)?;
         for span in self.pairs.iter() {
-            writeln!(f, "{}: {}", u32::from(span.0), span.1.into_raw())?;
+            writeln!(f, "{}: {:#?}", u32::from(span.0), span.1)?;
         }
         Ok(())
     }
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index b37aded..a6d5781 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -12,7 +12,7 @@
 [lib]
 
 [dependencies]
-backtrace = { version = "0.3.74", optional = true }
+backtrace = { version = "0.3.75", optional = true }
 jod-thread = "1.0.0"
 crossbeam-channel.workspace = true
 itertools.workspace = true
@@ -25,7 +25,7 @@
 
 [target.'cfg(windows)'.dependencies]
 miow = "0.6.0"
-windows-sys = { version = "0.59", features = ["Win32_Foundation"] }
+windows-sys = { version = "0.60", features = ["Win32_Foundation"] }
 
 [features]
 # Uncomment to enable for the whole crate graph
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 4c77048..9d3aaa8 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -27,7 +27,7 @@
 [dev-dependencies]
 rayon.workspace = true
 expect-test = "1.5.1"
-rustc_apfloat = "0.2.2"
+rustc_apfloat = "0.2.3"
 
 test-utils.workspace = true
 
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index dcf8534..f5530c5 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -30,6 +30,16 @@
     pub fn text(&self) -> TokenText<'_> {
         text_of_first_token(self.syntax())
     }
+    pub fn text_non_mutable(&self) -> &str {
+        fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
+            green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
+        }
+
+        match self.syntax().green() {
+            Cow::Borrowed(green_ref) => first_token(green_ref).text(),
+            Cow::Owned(_) => unreachable!(),
+        }
+    }
 }
 
 impl ast::NameRef {
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 8eb48f8..8937e53 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -1,5 +1,5 @@
 //! A set of high-level utility fixture methods to use in tests.
-use std::{mem, str::FromStr, sync};
+use std::{any::TypeId, mem, str::FromStr, sync};
 
 use base_db::{
     Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
@@ -677,6 +677,10 @@
     ) -> Result<TopSubtree, ProcMacroExpansionError> {
         Ok(subtree.clone())
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Expands to a macro_rules! macro, for issue #18089.
@@ -708,6 +712,10 @@
             #subtree
         })
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Pastes the attribute input as its output
@@ -728,6 +736,10 @@
             .cloned()
             .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 #[derive(Debug)]
@@ -759,6 +771,10 @@
         top_subtree_delimiter_mut.close = def_site;
         Ok(result)
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 #[derive(Debug)]
@@ -790,6 +806,10 @@
         traverse(&mut builder, input.iter());
         Ok(builder.build())
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Replaces every literal with an empty string literal and every identifier with its first letter,
@@ -830,6 +850,10 @@
             }
         }
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Reads ident type within string quotes, for issue #17479.
@@ -855,6 +879,10 @@
             #symbol()
         })
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Reads ident type within string quotes, for issue #17479.
@@ -906,6 +934,10 @@
             }
         })
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Reads ident type within string quotes, for issue #17479.
@@ -933,6 +965,10 @@
         }
         Ok(subtree.clone())
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
 
 // Generates a new type by adding a suffix to the original name
@@ -987,4 +1023,8 @@
 
         Ok(ret)
     }
+
+    fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
+        other.type_id() == TypeId::of::<Self>()
+    }
 }
diff --git a/docs/book/README.md b/docs/book/README.md
index 464ea02..0a3161f 100644
--- a/docs/book/README.md
+++ b/docs/book/README.md
@@ -19,7 +19,7 @@
 
 ## Making updates
 
-While not required, installing the mdbook binary can be helfpul in order to see the changes.
+While not required, installing the mdbook binary can be helpful in order to see the changes.
 Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/installation.html) to familiarize yourself with the tool.
 
 ## Generated documentation
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 0e07dad..4eb9cfc 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -1531,6 +1531,13 @@
 https://github.com/facebook/buck2/tree/main/integrations/rust-project.
 
 
+## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports}
+
+Default: `false`
+
+Exclude imports from symbol search.
+
+
 ## rust-analyzer.workspace.symbol.search.kind {#workspace.symbol.search.kind}
 
 Default: `"only_types"`
diff --git a/docs/book/src/contributing/README.md b/docs/book/src/contributing/README.md
index 05286b5..beb94cd 100644
--- a/docs/book/src/contributing/README.md
+++ b/docs/book/src/contributing/README.md
@@ -13,7 +13,7 @@
 It also explains the high-level layout of the source code.
 Do skim through that document.
 
-We also publish rustdoc docs to pages: https://rust-lang.github.io/rust-analyzer/ide/.
+We also publish rustdoc docs to <https://rust-lang.github.io/rust-analyzer/ide/>.
 Note though, that the internal documentation is very incomplete.
 
 Various organizational and process issues are discussed in this document.
@@ -30,7 +30,7 @@
 
 # Issue Labels
 
-* [good-first-issue](https://github.com/rust-lang/rust-analyzer/labels/good%20first%20issue)
+* [good-first-issue](https://github.com/rust-lang/rust-analyzer/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22)
   are good issues to get into the project.
 * [E-has-instructions](https://github.com/rust-lang/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
   issues have links to the code in question and tests.
diff --git a/docs/book/src/troubleshooting.md b/docs/book/src/troubleshooting.md
index 4092b9d..1b28414 100644
--- a/docs/book/src/troubleshooting.md
+++ b/docs/book/src/troubleshooting.md
@@ -46,5 +46,4 @@
 the standard library.
 
 If you want to go as far as to modify the source code to debug the
-problem, be sure to take a look at the [dev
-docs](https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev)!
+problem, be sure to take a look at the [contribution guide](contributing/index.html)!
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index ab43114..57d67a6 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -695,9 +695,9 @@
             }
         },
         "node_modules/@eslint/config-array/node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "version": "1.1.12",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+            "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -756,9 +756,9 @@
             }
         },
         "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "version": "1.1.12",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+            "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -1407,9 +1407,9 @@
             ]
         },
         "node_modules/@vscode/vsce/node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "version": "1.1.12",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+            "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -1599,9 +1599,9 @@
             "license": "ISC"
         },
         "node_modules/brace-expansion": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+            "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
             "license": "MIT",
             "dependencies": {
                 "balanced-match": "^1.0.0"
@@ -2981,9 +2981,9 @@
             }
         },
         "node_modules/eslint/node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "version": "1.1.12",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+            "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
diff --git a/editors/code/package.json b/editors/code/package.json
index c8c36cd..dcdb4fe 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -2894,6 +2894,16 @@
             {
                 "title": "workspace",
                 "properties": {
+                    "rust-analyzer.workspace.symbol.search.excludeImports": {
+                        "markdownDescription": "Exclude imports from symbol search.",
+                        "default": false,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
+                "title": "workspace",
+                "properties": {
                     "rust-analyzer.workspace.symbol.search.kind": {
                         "markdownDescription": "Workspace symbol search kind.",
                         "default": "only_types",
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml
index 1dc6d3c..35a5a4d 100644
--- a/lib/lsp-server/Cargo.toml
+++ b/lib/lsp-server/Cargo.toml
@@ -7,7 +7,7 @@
 edition = "2024"
 
 [dependencies]
-log = "0.4.26"
+log = "0.4.27"
 serde_json = "1.0.140"
 serde = { version = "1.0.219" }
 serde_derive = { version = "1.0.219" }
@@ -15,7 +15,7 @@
 
 [dev-dependencies]
 lsp-types = "=0.95"
-ctrlc = "3.4.5"
+ctrlc = "3.4.7"
 
 [lints]
 workspace = true
diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs
index 2749557..399d674 100644
--- a/lib/lsp-server/src/msg.rs
+++ b/lib/lsp-server/src/msg.rs
@@ -283,12 +283,12 @@
     buf.resize(size, 0);
     inp.read_exact(&mut buf)?;
     let buf = String::from_utf8(buf).map_err(invalid_data)?;
-    log::debug!("< {}", buf);
+    log::debug!("< {buf}");
     Ok(Some(buf))
 }
 
 fn write_msg_text(out: &mut dyn Write, msg: &str) -> io::Result<()> {
-    log::debug!("> {}", msg);
+    log::debug!("> {msg}");
     write!(out, "Content-Length: {}\r\n\r\n", msg.len())?;
     out.write_all(msg.as_bytes())?;
     out.flush()?;
diff --git a/lib/lsp-server/src/stdio.rs b/lib/lsp-server/src/stdio.rs
index c558b6c..eccc89f 100644
--- a/lib/lsp-server/src/stdio.rs
+++ b/lib/lsp-server/src/stdio.rs
@@ -38,7 +38,7 @@
             while let Some(msg) = Message::read(&mut stdin)? {
                 let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit());
 
-                debug!("sending message {:#?}", msg);
+                debug!("sending message {msg:#?}");
                 if let Err(e) = reader_sender.send(msg) {
                     return Err(io::Error::other(e));
                 }
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index bb7d83c..8cd5811 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -9,14 +9,14 @@
 [dependencies]
 anyhow.workspace = true
 directories = "6.0"
-flate2 = "1.1.0"
+flate2 = "1.1.2"
 write-json = "0.1.4"
 xshell.workspace = true
 xflags = "0.3.2"
 time = { version = "0.3", default-features = false }
-zip = { version = "3.0", default-features = false, features = ["deflate-flate2", "time"] }
+zip = { version = "4.0", default-features = false, features = ["deflate-flate2", "time"] }
 stdx.workspace = true
-proc-macro2 = "1.0.94"
+proc-macro2 = "1.0.95"
 quote = "1.0.40"
 ungrammar = "1.16.1"
 either.workspace = true