diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f436c236dc9..767ea29d636 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - name: mingw-check-tidy os: ubuntu-20.04-4core-16gb env: {} - - name: x86_64-gnu-llvm-16 + - name: x86_64-gnu-llvm-17 env: ENABLE_GCC_CODEGEN: "1" os: ubuntu-20.04-16core-64gb @@ -323,10 +323,6 @@ jobs: env: RUST_BACKTRACE: 1 os: ubuntu-20.04-8core-32gb - - name: x86_64-gnu-llvm-16 - env: - RUST_BACKTRACE: 1 - os: ubuntu-20.04-8core-32gb - name: x86_64-gnu-nopt os: ubuntu-20.04-4core-16gb env: {} diff --git a/Cargo.lock b/Cargo.lock index 927e93f27eb..3110f32ade9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" dependencies = [ "backtrace", ] @@ -246,7 +246,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -293,9 +293,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "basic-toml" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" dependencies = [ "serde", ] @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytecount" @@ -484,10 +484,16 @@ dependencies = [ ] [[package]] -name = "chrono" -version = "0.4.34" +name = "cfg_aliases" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -508,9 +514,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", "clap_derive", @@ -528,9 +534,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -550,14 +556,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -584,7 +590,7 @@ dependencies = [ "regex", "rustc_tools_util", "serde", - "syn 2.0.52", + "syn 2.0.53", "tempfile", "termize", "tester", @@ -664,9 +670,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -884,9 +890,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ "nix", "windows-sys 0.52.0", @@ -943,7 +949,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -954,7 +960,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -969,7 +975,7 @@ version = "0.1.78" dependencies = [ "itertools 0.12.1", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1010,7 +1016,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1020,7 +1026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1043,7 +1049,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1132,7 +1138,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1217,9 +1223,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", @@ -1478,7 +1484,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1603,9 +1609,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -1653,6 +1659,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1712,9 +1724,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1910,7 +1922,7 @@ checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2090,9 +2102,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2188,9 +2200,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -2524,18 +2536,19 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.4.2", "cfg-if", + "cfg_aliases", "libc", ] @@ -2676,7 +2689,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2869,7 +2882,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -2992,9 +3005,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -3261,9 +3274,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" dependencies = [ "base64", "bytes", @@ -3903,7 +3916,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "unic-langid", ] @@ -4038,7 +4051,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "synstructure", ] @@ -4184,7 +4197,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "synstructure", ] @@ -4809,7 +4822,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -4981,7 +4994,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5259,7 +5272,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -5288,9 +5301,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -5311,14 +5324,14 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] name = "sysinfo" -version = "0.30.6" +version = "0.30.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6746919caf9f2a85bff759535664c060109f21975c5ac2e8652e60102bd4d196" +checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18" dependencies = [ "cfg-if", "core-foundation-sys", @@ -5475,22 +5488,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5609,7 +5622,6 @@ dependencies = [ "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", "socket2", "windows-sys 0.48.0", @@ -5714,7 +5726,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -5931,7 +5943,7 @@ checksum = "fea2a4c80deb4fb3ca51f66b5e2dd91e3642bbce52234bcf22e41668281208e4" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.52", + "syn 2.0.53", "unic-langid-impl", ] @@ -6144,9 +6156,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -6154,24 +6166,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -6181,9 +6193,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6191,22 +6203,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" @@ -6229,9 +6241,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -6288,7 +6300,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "syn 2.0.52", + "syn 2.0.53", "windows-metadata", ] @@ -6542,7 +6554,7 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "synstructure", ] @@ -6563,7 +6575,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -6583,7 +6595,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "synstructure", ] @@ -6606,7 +6618,7 @@ checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] diff --git a/INSTALL.md b/INSTALL.md index 03e7a3431a5..a23ea4f1eee 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -154,11 +154,11 @@ toolchain. however this is not recommended as it's excrutiatingly slow, and not frequently tested for compatability. -2. Start a MINGW64 or MINGW32 shell (depending on whether you want 32-bit +3. Start a MINGW64 or MINGW32 shell (depending on whether you want 32-bit or 64-bit Rust) either from your start menu, or by running `mingw64.exe` or `mingw32.exe` from your MSYS2 installation directory (e.g. `C:\msys64`). -3. From this terminal, install the required tools: +4. From this terminal, install the required tools: ```sh # Update package mirrors (may be needed if you have a fresh install of MSYS2) @@ -178,7 +178,7 @@ toolchain. mingw-w64-x86_64-ninja ``` -4. Navigate to Rust's source code (or clone it), then build it: +5. Navigate to Rust's source code (or clone it), then build it: ```sh python x.py setup dist && python x.py build && python x.py install diff --git a/RELEASES.md b/RELEASES.md index 20e317a4d23..922afdd3e18 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,120 @@ +Version 1.77.0 (2024-03-21) +========================== + + + +Language +-------- + +- [Reveal opaque types within the defining body for exhaustiveness checking.](https://github.com/rust-lang/rust/pull/116821/) +- [Stabilize C-string literals.](https://github.com/rust-lang/rust/pull/117472/) +- [Stabilize THIR unsafeck.](https://github.com/rust-lang/rust/pull/117673/) +- [Add lint `static_mut_refs` to warn on references to mutable statics.](https://github.com/rust-lang/rust/pull/117556/) +- [Support async recursive calls (as long as they have indirection).](https://github.com/rust-lang/rust/pull/117703/) +- [Undeprecate lint `unstable_features` and make use of it in the compiler.](https://github.com/rust-lang/rust/pull/118639/) +- [Make inductive cycles in coherence ambiguous always.](https://github.com/rust-lang/rust/pull/118649/) +- [Get rid of type-driven traversal in const-eval interning](https://github.com/rust-lang/rust/pull/119044/), + only as a [future compatiblity lint](https://github.com/rust-lang/rust/pull/122204) for now. +- [Deny braced macro invocations in let-else.](https://github.com/rust-lang/rust/pull/119062/) + + + +Compiler +-------- + +- [Include lint `soft_unstable` in future breakage reports.](https://github.com/rust-lang/rust/pull/116274/) +- [Make `i128` and `u128` 16-byte aligned on x86-based targets.](https://github.com/rust-lang/rust/pull/116672/) +- [Use `--verbose` in diagnostic output.](https://github.com/rust-lang/rust/pull/119129/) +- [Improve spacing between printed tokens.](https://github.com/rust-lang/rust/pull/120227/) +- [Merge the `unused_tuple_struct_fields` lint into `dead_code`.](https://github.com/rust-lang/rust/pull/118297/) +- [Error on incorrect implied bounds in well-formedness check](https://github.com/rust-lang/rust/pull/118553/), + with a temporary exception for Bevy. +- [Fix coverage instrumentation/reports for non-ASCII source code.](https://github.com/rust-lang/rust/pull/119033/) +- [Fix `fn`/`const` items implied bounds and well-formedness check.](https://github.com/rust-lang/rust/pull/120019/) +- [Promote `riscv32{im|imafc}-unknown-none-elf` targets to tier 2.](https://github.com/rust-lang/rust/pull/118704/) +- Add several new tier 3 targets: + - [`aarch64-unknown-illumos`](https://github.com/rust-lang/rust/pull/112936/) + - [`hexagon-unknown-none-elf`](https://github.com/rust-lang/rust/pull/117601/) + - [`riscv32imafc-esp-espidf`](https://github.com/rust-lang/rust/pull/119738/) + - [`riscv32im-risc0-zkvm-elf`](https://github.com/rust-lang/rust/pull/117958/) + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + + + +Libraries +--------- + +- [Implement `From<&[T; N]>` for `Cow<[T]>`.](https://github.com/rust-lang/rust/pull/113489/) +- [Remove special-case handling of `vec.split_off(0)`.](https://github.com/rust-lang/rust/pull/119917/) + + + +Stabilized APIs +--------------- + +- [`array::each_ref`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_ref) +- [`array::each_mut`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut) +- [`core::net`](https://doc.rust-lang.org/stable/core/net/index.html) +- [`f32::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round_ties_even) +- [`f64::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round_ties_even) +- [`mem::offset_of!`](https://doc.rust-lang.org/stable/std/mem/macro.offset_of.html) +- [`slice::first_chunk`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.first_chunk) +- [`slice::first_chunk_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.first_chunk_mut) +- [`slice::split_first_chunk`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_first_chunk) +- [`slice::split_first_chunk_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_first_chunk_mut) +- [`slice::last_chunk`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.last_chunk) +- [`slice::last_chunk_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.last_chunk_mut) +- [`slice::split_last_chunk`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_last_chunk) +- [`slice::split_last_chunk_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_last_chunk_mut) +- [`slice::chunk_by`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunk_by) +- [`slice::chunk_by_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunk_by_mut) +- [`Bound::map`](https://doc.rust-lang.org/stable/std/ops/enum.Bound.html#method.map) +- [`File::create_new`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.create_new) +- [`Mutex::clear_poison`](https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html#method.clear_poison) +- [`RwLock::clear_poison`](https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html#method.clear_poison) + + + +Cargo +----- + +- [Extend the build directive syntax with `cargo::`.](https://github.com/rust-lang/cargo/pull/12201/) +- [Stabilize metadata `id` format as `PackageIDSpec`.](https://github.com/rust-lang/cargo/pull/12914/) +- [Pull out `cargo-util-schemas` as a crate.](https://github.com/rust-lang/cargo/pull/13178/) +- [Strip all debuginfo when debuginfo is not requested.](https://github.com/rust-lang/cargo/pull/13257/) +- [Inherit jobserver from env for all kinds of runners.](https://github.com/rust-lang/cargo/pull/12776/) +- [Deprecate rustc plugin support in cargo.](https://github.com/rust-lang/cargo/pull/13248/) + + + +Rustdoc +----- + +- [Allows links in markdown headings.](https://github.com/rust-lang/rust/pull/117662/) +- [Search for tuples and unit by type with `()`.](https://github.com/rust-lang/rust/pull/118194/) +- [Clean up the source sidebar's hide button.](https://github.com/rust-lang/rust/pull/119066/) +- [Prevent JS injection from `localStorage`.](https://github.com/rust-lang/rust/pull/120250/) + + + +Misc +---- + +- [Recommend version-sorting for all sorting in style guide.](https://github.com/rust-lang/rust/pull/115046/) + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Add more weirdness to `weird-exprs.rs`.](https://github.com/rust-lang/rust/pull/119028/) + Version 1.76.0 (2024-02-08) ========================== diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 6e42cf37b86..ef60a1c9b5b 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -11,7 +11,7 @@ #![doc(rust_logo)] #![allow(internal_features)] #![feature(rustdoc_internals)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d75ff4565e6..825f8dad8e4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -755,7 +755,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>( } AssocItemKind::Delegation(box Delegation { id, qself, path, body }) => { if let Some(qself) = qself { - visitor.visit_ty(&qself.ty); + try_visit!(visitor.visit_ty(&qself.ty)); } try_visit!(visitor.visit_path(path, *id)); visit_opt!(visitor, visit_block, body); @@ -994,7 +994,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V ExprKind::InlineAsm(asm) => try_visit!(visitor.visit_inline_asm(asm)), ExprKind::FormatArgs(f) => try_visit!(visitor.visit_format_args(f)), ExprKind::OffsetOf(container, fields) => { - visitor.visit_ty(container); + try_visit!(visitor.visit_ty(container)); walk_list!(visitor, visit_ident, fields.iter().copied()); } ExprKind::Yield(optional_expression) => { diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 3f84e6b100d..d2c3b8b2d56 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -604,8 +604,7 @@ fn may_contain_yield_point(e: &ast::Expr) -> bool { if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind { ControlFlow::Break(()) } else { - visit::walk_expr(self, e); - ControlFlow::Continue(()) + visit::walk_expr(self, e) } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 4db8ffd3e54..2c396a64789 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -463,13 +463,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { constraint.span, "return type notation is experimental" ); - } else { - gate!( - &self, - associated_type_bounds, - constraint.span, - "associated type bounds are unstable" - ); } } visit::walk_assoc_constraint(self, constraint) @@ -615,7 +608,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); - gate_all_legacy_dont_use!(associated_type_bounds, "associated type bounds are unstable"); // Despite being a new feature, `where T: Trait`, which is RTN syntax now, // used to be gated under associated_type_bounds, which are right above, so RTN needs to // be too. diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f81c74f3482..0109f188772 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; +use rustc_hir::intravisit::{walk_block, walk_expr, Map, Visitor}; use rustc_hir::{CoroutineDesugaring, PatField}; use rustc_hir::{CoroutineKind, CoroutineSource, LangItem}; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -447,8 +447,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_note( span, format!( - "consider changing this parameter type in {descr} `{ident}` to \ - borrow instead if owning the value isn't necessary", + "consider changing this parameter type in {descr} `{ident}` to borrow \ + instead if owning the value isn't necessary", ), ); } @@ -463,7 +463,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = move_spans { // We already suggest cloning for these cases in `explain_captures`. - } else { + } else if self.suggest_hoisting_call_outside_loop(err, expr) { + // The place where the the type moves would be misleading to suggest clone. + // #121466 self.suggest_cloning(err, ty, expr, move_span); } } @@ -747,6 +749,234 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { true } + /// In a move error that occurs on a call wihtin a loop, we try to identify cases where cloning + /// the value would lead to a logic error. We infer these cases by seeing if the moved value is + /// part of the logic to break the loop, either through an explicit `break` or if the expression + /// is part of a `while let`. + fn suggest_hoisting_call_outside_loop(&self, err: &mut Diag<'_>, expr: &hir::Expr<'_>) -> bool { + let tcx = self.infcx.tcx; + let mut can_suggest_clone = true; + + // If the moved value is a locally declared binding, we'll look upwards on the expression + // tree until the scope where it is defined, and no further, as suggesting to move the + // expression beyond that point would be illogical. + let local_hir_id = if let hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Local(local_hir_id), .. }, + )) = expr.kind + { + Some(local_hir_id) + } else { + // This case would be if the moved value comes from an argument binding, we'll just + // look within the entire item, that's fine. + None + }; + + /// This will allow us to look for a specific `HirId`, in our case `local_hir_id` where the + /// binding was declared, within any other expression. We'll use it to search for the + /// binding declaration within every scope we inspect. + struct Finder { + hir_id: hir::HirId, + found: bool, + } + impl<'hir> Visitor<'hir> for Finder { + fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) { + if pat.hir_id == self.hir_id { + self.found = true; + } + hir::intravisit::walk_pat(self, pat); + } + fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { + if ex.hir_id == self.hir_id { + self.found = true; + } + hir::intravisit::walk_expr(self, ex); + } + } + // The immediate HIR parent of the moved expression. We'll look for it to be a call. + let mut parent = None; + // The top-most loop where the moved expression could be moved to a new binding. + let mut outer_most_loop: Option<&hir::Expr<'_>> = None; + for (_, node) in tcx.hir().parent_iter(expr.hir_id) { + let e = match node { + hir::Node::Expr(e) => e, + hir::Node::Local(hir::Local { els: Some(els), .. }) => { + let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] }; + finder.visit_block(els); + if !finder.found_breaks.is_empty() { + // Don't suggest clone as it could be will likely end in an infinite + // loop. + // let Some(_) = foo(non_copy.clone()) else { break; } + // --- ^^^^^^^^ ----- + can_suggest_clone = false; + } + continue; + } + _ => continue, + }; + if let Some(&hir_id) = local_hir_id { + let mut finder = Finder { hir_id, found: false }; + finder.visit_expr(e); + if finder.found { + // The current scope includes the declaration of the binding we're accessing, we + // can't look up any further for loops. + break; + } + } + if parent.is_none() { + parent = Some(e); + } + match e.kind { + hir::ExprKind::Let(_) => { + match tcx.parent_hir_node(e.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::If(cond, ..), .. + }) => { + let mut finder = Finder { hir_id: expr.hir_id, found: false }; + finder.visit_expr(cond); + if finder.found { + // The expression where the move error happened is in a `while let` + // condition Don't suggest clone as it will likely end in an + // infinite loop. + // while let Some(_) = foo(non_copy.clone()) { } + // --------- ^^^^^^^^ + can_suggest_clone = false; + } + } + _ => {} + } + } + hir::ExprKind::Loop(..) => { + outer_most_loop = Some(e); + } + _ => {} + } + } + let loop_count: usize = tcx + .hir() + .parent_iter(expr.hir_id) + .map(|(_, node)| match node { + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Loop(..), .. }) => 1, + _ => 0, + }) + .sum(); + + let sm = tcx.sess.source_map(); + if let Some(in_loop) = outer_most_loop { + let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] }; + finder.visit_expr(in_loop); + // All of the spans for `break` and `continue` expressions. + let spans = finder + .found_breaks + .iter() + .chain(finder.found_continues.iter()) + .map(|(_, span)| *span) + .filter(|span| { + !matches!( + span.desugaring_kind(), + Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop) + ) + }) + .collect::>(); + // All of the spans for the loops above the expression with the move error. + let loop_spans: Vec<_> = tcx + .hir() + .parent_iter(expr.hir_id) + .filter_map(|(_, node)| match node { + hir::Node::Expr(hir::Expr { span, kind: hir::ExprKind::Loop(..), .. }) => { + Some(*span) + } + _ => None, + }) + .collect(); + // It is possible that a user written `break` or `continue` is in the wrong place. We + // point them out at the user for them to make a determination. (#92531) + if !spans.is_empty() && loop_count > 1 { + // Getting fancy: if the spans of the loops *do not* overlap, we only use the line + // number when referring to them. If there *are* overlaps (multiple loops on the + // same line) then we use the more verbose span output (`file.rs:col:ll`). + let mut lines: Vec<_> = + loop_spans.iter().map(|sp| sm.lookup_char_pos(sp.lo()).line).collect(); + lines.sort(); + lines.dedup(); + let fmt_span = |span: Span| { + if lines.len() == loop_spans.len() { + format!("line {}", sm.lookup_char_pos(span.lo()).line) + } else { + sm.span_to_diagnostic_string(span) + } + }; + let mut spans: MultiSpan = spans.clone().into(); + // Point at all the `continue`s and explicit `break`s in the relevant loops. + for (desc, elements) in [ + ("`break` exits", &finder.found_breaks), + ("`continue` advances", &finder.found_continues), + ] { + for (destination, sp) in elements { + if let Ok(hir_id) = destination.target_id + && let hir::Node::Expr(expr) = tcx.hir().hir_node(hir_id) + && !matches!( + sp.desugaring_kind(), + Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop) + ) + { + spans.push_span_label( + *sp, + format!("this {desc} the loop at {}", fmt_span(expr.span)), + ); + } + } + } + // Point at all the loops that are between this move and the parent item. + for span in loop_spans { + spans.push_span_label(sm.guess_head_span(span), ""); + } + + // note: verify that your loop breaking logic is correct + // --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17 + // | + // 28 | for foo in foos { + // | --------------- + // ... + // 33 | for bar in &bars { + // | ---------------- + // ... + // 41 | continue; + // | ^^^^^^^^ this `continue` advances the loop at line 33 + err.span_note(spans, "verify that your loop breaking logic is correct"); + } + if let Some(parent) = parent + && let hir::ExprKind::MethodCall(..) | hir::ExprKind::Call(..) = parent.kind + { + // FIXME: We could check that the call's *parent* takes `&mut val` to make the + // suggestion more targeted to the `mk_iter(val).next()` case. Maybe do that only to + // check for wheter to suggest `let value` or `let mut value`. + + let span = in_loop.span; + if !finder.found_breaks.is_empty() + && let Ok(value) = sm.span_to_snippet(parent.span) + { + // We know with high certainty that this move would affect the early return of a + // loop, so we suggest moving the expression with the move out of the loop. + let indent = if let Some(indent) = sm.indentation_before(span) { + format!("\n{indent}") + } else { + " ".to_string() + }; + err.multipart_suggestion( + "consider moving the expression out of the loop so it is only moved once", + vec![ + (parent.span, "value".to_string()), + (span.shrink_to_lo(), format!("let mut value = {value};{indent}")), + ], + Applicability::MaybeIncorrect, + ); + } + } + } + can_suggest_clone + } + fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) { let tcx = self.infcx.tcx; // Try to find predicates on *generic params* that would allow copying `ty` @@ -3688,6 +3918,28 @@ impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> { } } +/// Look for `break` expressions within any arbitrary expressions. We'll do this to infer +/// whether this is a case where the moved value would affect the exit of a loop, making it +/// unsuitable for a `.clone()` suggestion. +struct BreakFinder { + found_breaks: Vec<(hir::Destination, Span)>, + found_continues: Vec<(hir::Destination, Span)>, +} +impl<'hir> Visitor<'hir> for BreakFinder { + fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { + match ex.kind { + hir::ExprKind::Break(destination, _) => { + self.found_breaks.push((destination, ex.span)); + } + hir::ExprKind::Continue(destination) => { + self.found_continues.push((destination, ex.span)); + } + _ => {} + } + hir::intravisit::walk_expr(self, ex); + } +} + /// Given a set of spans representing statements initializing the relevant binding, visit all the /// function expressions looking for branching code paths that *do not* initialize the binding. struct ConditionVisitor<'b> { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 415ba095a1b..9f4f88b2b93 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -4,7 +4,7 @@ #![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(assert_matches)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(let_chains)] diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 39fa277fedc..fc9b0f6ef02 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -74,7 +74,7 @@ pub(crate) fn eval_mir_constant<'tcx>( let cv = fx.monomorphize(constant.const_); // This cannot fail because we checked all required_consts in advance. let val = cv - .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) + .eval(fx.tcx, ty::ParamEnv::reveal_all(), constant.span) .expect("erroneous constant missed by mono item collection"); (val, cv.ty()) } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 8b86a116df1..c802d9bbefb 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -728,8 +728,10 @@ fn codegen_regular_intrinsic_call<'tcx>( | sym::variant_count => { intrinsic_args!(fx, args => (); intrinsic); - let const_val = - fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); + let const_val = fx + .tcx + .const_eval_instance(ParamEnv::reveal_all(), instance, source_info.span) + .unwrap(); let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty); ret.write_cvalue(fx, val); } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 79da970a58e..4d55a95aa9d 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -131,7 +131,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let idx = generic_args[2] .expect_const() - .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(span)) + .eval(fx.tcx, ty::ParamEnv::reveal_all(), span) .unwrap() .unwrap_branch(); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a4ee3015b8d..345c22394f2 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -19,11 +19,11 @@ #![feature( rustc_private, decl_macro, - associated_type_bounds, never_type, trusted_len, hash_raw_entry )] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![allow(broken_intra_doc_links)] #![recursion_limit = "256"] #![warn(rust_2018_idioms)] diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f89c8c9f836..c3f17563b0a 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -126,17 +126,6 @@ pub unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (17, 0, 0) { - if sess.target.arch.starts_with("powerpc") { - // LLVM 17 specifies function pointer alignment for ppc: - // https://reviews.llvm.org/D147016 - target_data_layout = target_data_layout - .replace("-Fn32", "") - .replace("-Fi32", "") - .replace("-Fn64", "") - .replace("-Fi64", ""); - } - } if llvm_version < (18, 0, 0) { if sess.target.arch == "x86" || sess.target.arch == "x86_64" { // LLVM 18 adjusts i128 to be 128-bit aligned on x86 variants. diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 467e02d55e3..71b69a94e99 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1129,7 +1129,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_shuffle_generic { let idx = fn_args[2] .expect_const() - .eval(tcx, ty::ParamEnv::reveal_all(), Some(span)) + .eval(tcx, ty::ParamEnv::reveal_all(), span) .unwrap() .unwrap_branch(); let n = idx.len() as u64; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d34c289887e..284bc74d5c4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1519,7 +1519,7 @@ extern "C" { #[link(name = "llvm-wrapper", kind = "static")] extern "C" { - pub fn LLVMRustInstallFatalErrorHandler(); + pub fn LLVMRustInstallErrorHandlers(); pub fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index e32c38644aa..c9e62e504ae 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -49,7 +49,7 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_c_strs = Vec::with_capacity(n_args + 1); let mut llvm_args = Vec::with_capacity(n_args + 1); - llvm::LLVMRustInstallFatalErrorHandler(); + llvm::LLVMRustInstallErrorHandlers(); // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog // box for the purpose of launching a debugger. However, on CI this will // cause it to hang until it times out, which can take several hours. diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 5bd7442822a..fcd7fa9247b 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -19,6 +19,7 @@ use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Mutability} use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; +use rustc_span::DUMMY_SP; use rustc_target::abi::Integer; use smallvec::SmallVec; @@ -704,7 +705,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S // avoiding collisions and will make the emitted type names shorter. let hash_short = tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), None).unwrap(); + let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap(); hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher)); hasher.finish::() }); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 92f0be541c0..9be8dcf166d 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -4,7 +4,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(let_chains)] diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9bb2a528265..02e7bb05b77 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1688,7 +1688,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn try_llbb(&mut self, bb: mir::BasicBlock) -> Option { match self.cached_llbbs[bb] { CachedLlbb::None => { - // FIXME(eddyb) only name the block if `fewer_names` is `false`. let llbb = Bx::append_block(self.cx, self.llfn, &format!("{bb:?}")); self.cached_llbbs[bb] = CachedLlbb::Some(llbb); Some(llbb) diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index ff899c50b3d..c6260d35916 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -24,7 +24,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // `MirUsedCollector` visited all required_consts before codegen began, so if we got here // there can be no more constants that fail to evaluate. self.monomorphize(constant.const_) - .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span)) + .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), constant.span) .expect("erroneous constant missed by mono item collection") } @@ -56,11 +56,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { other => span_bug!(constant.span, "{other:#?}"), }; let uv = self.monomorphize(uv); - self.cx.tcx().const_eval_resolve_for_typeck( - ty::ParamEnv::reveal_all(), - uv, - Some(constant.span), - ) + self.cx.tcx().const_eval_resolve_for_typeck(ty::ParamEnv::reveal_all(), uv, constant.span) } /// process constant containing SIMD shuffle indices diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 1d1826d9844..ba023f96107 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::variant_count => { let value = bx .tcx() - .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) + .const_eval_instance(ty::ParamEnv::reveal_all(), instance, span) .unwrap(); OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx) } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 09e9cc4b35d..730e8977803 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -826,7 +826,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { for &const_ in &body.required_consts { let c = self .instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; - c.eval(*self.tcx, self.param_env, Some(const_.span)).map_err(|err| { + c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| { err.emit_note(*self.tcx); err })?; @@ -1174,7 +1174,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn eval_mir_constant( &self, val: &mir::Const<'tcx>, - span: Option, + span: Span, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b68bfb4211d..748c7dce5a3 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -153,9 +153,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::type_name => Ty::new_static_str(self.tcx.tcx), _ => bug!(), }; - let val = self.ctfe_query(|tcx| { - tcx.const_eval_global_id(self.param_env, gid, Some(tcx.span)) - })?; + let val = + self.ctfe_query(|tcx| tcx.const_eval_global_id(self.param_env, gid, tcx.span))?; let val = self.const_val_to_op(val, ty, Some(dest.layout))?; self.copy_op(&val, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index afc4a80c283..827d8fd9417 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -525,7 +525,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { fn eval_mir_constant( ecx: &InterpCx<'mir, 'tcx, Self>, val: mir::Const<'tcx>, - span: Option, + span: Span, layout: Option>, eval: F, ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>> @@ -533,7 +533,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { F: Fn( &InterpCx<'mir, 'tcx, Self>, mir::Const<'tcx>, - Option, + Span, Option>, ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>, { diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index e49067a2f00..dbc6a317640 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -741,7 +741,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all // checked yet. // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. - self.eval_mir_constant(&c, Some(constant.span), layout)? + self.eval_mir_constant(&c, constant.span, layout)? } }; trace!("{:?}: {:?}", mir_op, op); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 15720f25c5c..e87e60f62dc 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -99,7 +99,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { #[allow(rustc::untranslatable_diagnostic)] fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self; - let ConstCx { tcx, param_env, .. } = *ccx; + let ConstCx { tcx, param_env, body, .. } = *ccx; let diag_trait = |err, self_ty: Ty<'_>, trait_id| { let trait_ref = TraitRef::from_method(tcx, trait_id, args); @@ -297,10 +297,12 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { ccx.const_kind(), )); - if let Some(feature) = feature - && ccx.tcx.sess.is_nightly_build() - { - err.help(format!("add `#![feature({feature})]` to the crate attributes to enable",)); + if let Some(feature) = feature { + ccx.tcx.disabled_nightly_features( + &mut err, + body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)), + [(String::new(), feature)], + ); } if let ConstContext::Static(_) = ccx.const_kind() { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index f26c8f8592d..68270dab284 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -341,7 +341,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { // FIXME(JakobDegen) The validator should check that `self.mir_phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. - if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) { + if matches!(kind, RetagKind::TwoPhase) { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } @@ -1272,7 +1272,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME(JakobDegen) The validator should check that `self.mir_phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. - if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) { + if matches!(kind, RetagKind::TwoPhase) { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md index 057a0b1645c..cd981db1058 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0719.md +++ b/compiler/rustc_error_codes/src/error_codes/E0719.md @@ -3,8 +3,6 @@ An associated type value was specified more than once. Erroneous code example: ```compile_fail,E0719 -#![feature(associated_type_bounds)] - trait FooTrait {} trait BarTrait {} @@ -19,8 +17,6 @@ specify the associated type with the new trait. Corrected example: ``` -#![feature(associated_type_bounds)] - trait FooTrait {} trait BarTrait {} trait FooBarTrait: FooTrait + BarTrait {} diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index e550f7242c3..0b8f75bc2ca 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,7 +1,7 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![feature(array_windows)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(associated_type_defaults)] #![feature(if_let_guard)] #![feature(let_chains)] diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 7e5197f16f9..5957d03853d 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -59,6 +59,8 @@ declare_features! ( (accepted, asm_sym, "1.66.0", Some(93333)), /// Allows the definition of associated constants in `trait` or `impl` blocks. (accepted, associated_consts, "1.20.0", Some(29646)), + /// Allows the user of associated type bounds. + (accepted, associated_type_bounds, "CURRENT_RUSTC_VERSION", Some(52662)), /// Allows using associated `type`s in `trait`s. (accepted, associated_types, "1.0.0", None), /// Allows free and inherent `async fn`s, `async` blocks, and `.await` expressions. @@ -203,6 +205,8 @@ declare_features! ( (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872)), /// Allows referencing `Self` and projections in impl-trait. (accepted, impl_trait_projections, "1.74.0", Some(103532)), + /// Allows using imported `main` function + (accepted, imported_main, "CURRENT_RUSTC_VERSION", Some(28937)), /// Allows using `a..=b` and `..=b` as inclusive range syntaxes. (accepted, inclusive_range_syntax, "1.26.0", Some(28237)), /// Allows inferring outlives requirements (RFC 2093). diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c3f216936df..528fabc8d9c 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -351,8 +351,6 @@ declare_features! ( (unstable, asm_unwind, "1.58.0", Some(93334)), /// Allows users to enforce equality of associated constants `TraitImpl`. (unstable, associated_const_equality, "1.58.0", Some(92827)), - /// Allows the user of associated type bounds. - (unstable, associated_type_bounds, "1.34.0", Some(52662)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), /// Allows `async || body` closures. @@ -495,8 +493,6 @@ declare_features! ( (unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. (unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)), - /// Allows using imported `main` function - (unstable, imported_main, "1.53.0", Some(28937)), /// Allows associated types in inherent impls. (incomplete, inherent_associated_types, "1.52.0", Some(8995)), /// Allow anonymous constants from an inline `const` block diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e21d31238e0..186b8716d9a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2049,7 +2049,7 @@ impl LoopSource { } } -#[derive(Copy, Clone, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)] pub enum LoopIdError { OutsideLoopScope, UnlabeledCfInWhileCondition, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index fbbad38d17f..186bb234a45 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -899,7 +899,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => { try_visit!(visitor.visit_ty(ty)); if let Some(ref default) = default { - visitor.visit_const_param_default(param.hir_id, default); + try_visit!(visitor.visit_const_param_default(param.hir_id, default)); } } } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 1c68de51ba6..9bf4d63267a 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -118,6 +118,11 @@ hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed .label = overflowed on value after {$discr} .note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome +hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding = + the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters + .label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}` + .var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here + hir_analysis_field_already_declared = field `{$field_name}` is already declared .label = field already declared @@ -316,6 +321,22 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur .label = `impl Trait` implicitly captures all lifetimes in scope .note = lifetime declared here +hir_analysis_param_in_ty_of_assoc_const_binding = + the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] generic parameters + } + .label = its type must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] the {$param_def_kind} `{$param_name}` + } + .param_defined_here_label = {$param_category -> + [synthetic] the `impl Trait` is specified here + *[normal] the {$param_def_kind} `{$param_name}` is defined here + } + hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it @@ -432,6 +453,8 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de .label = needs at most one field with non-trivial size or alignment, but has {$field_count} .labels = this field has non-zero size or requires alignment +hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}` + hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index c6942b0f456..3067e2d0b71 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -1,12 +1,15 @@ -use rustc_data_structures::fx::FxIndexMap; +use std::ops::ControlFlow; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::{self as ty, Ty}; +use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_trait_selection::traits; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; @@ -433,14 +436,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { binding.kind { let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); - // Since the arguments passed to the alias type above may contain early-bound - // generic parameters, the instantiated type may contain some as well. - // Therefore wrap it in `EarlyBinder`. - // FIXME(fmease): Reject escaping late-bound vars. - tcx.feed_anon_const_type( - anon_const.def_id, - ty::EarlyBinder::bind(ty.skip_binder()), - ); + let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id); + tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); } alias_ty @@ -530,3 +527,167 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { Ok(()) } } + +/// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings. +/// +/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the +/// arrival of *generic const generics*[^1]. +/// +/// It might actually be possible that we can already support early-bound generic params +/// in such types if we just lifted some more checks in other places, too, for example +/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should +/// probably gate this behind another feature flag. +/// +/// [^1]: . +fn check_assoc_const_binding_type<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_const: Ident, + ty: ty::Binder<'tcx, Ty<'tcx>>, + hir_id: hir::HirId, +) -> Ty<'tcx> { + // We can't perform the checks for early-bound params during name resolution unlike E0770 + // because this information depends on *type* resolution. + // We can't perform these checks in `resolve_bound_vars` either for the same reason. + // Consider the trait ref `for<'a> Trait<'a, C = { &0 }>`. We need to know the fully + // resolved type of `Trait::C` in order to know if it references `'a` or not. + + let ty = ty.skip_binder(); + if !ty.has_param() && !ty.has_escaping_bound_vars() { + return ty; + } + + let mut collector = GenericParamAndBoundVarCollector { + tcx, + params: Default::default(), + vars: Default::default(), + depth: ty::INNERMOST, + }; + let mut guar = ty.visit_with(&mut collector).break_value(); + + let ty_note = ty + .make_suggestable(tcx, false) + .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); + + let enclosing_item_owner_id = tcx + .hir() + .parent_owner_iter(hir_id) + .find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id)) + .unwrap(); + let generics = tcx.generics_of(enclosing_item_owner_id); + for index in collector.params { + let param = generics.param_at(index as _, tcx); + let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper; + guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + param_category: if is_self_param { + "self" + } else if param.kind.is_synthetic() { + "synthetic" + } else { + "normal" + }, + param_defined_here_label: + (!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()), + ty_note, + })); + } + for (var_def_id, var_name) in collector.vars { + guar.get_or_insert(tcx.dcx().emit_err( + crate::errors::EscapingBoundVarInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + var_name, + var_def_kind: tcx.def_descr(var_def_id), + var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(), + ty_note, + }, + )); + } + + let guar = guar.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty")); + Ty::new_error(tcx, guar) +} + +struct GenericParamAndBoundVarCollector<'tcx> { + tcx: TyCtxt<'tcx>, + params: FxIndexSet, + vars: FxIndexSet<(DefId, Symbol)>, + depth: ty::DebruijnIndex, +} + +impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx> { + type Result = ControlFlow; + + fn visit_binder>>( + &mut self, + binder: &ty::Binder<'tcx, T>, + ) -> Self::Result { + self.depth.shift_in(1); + let result = binder.super_visit_with(self); + self.depth.shift_out(1); + result + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + match ty.kind() { + ty::Param(param) => { + self.params.insert(param.index); + } + ty::Bound(db, bt) if *db >= self.depth => { + self.vars.insert(match bt.kind { + ty::BoundTyKind::Param(def_id, name) => (def_id, name), + ty::BoundTyKind::Anon => { + let reported = self + .tcx + .dcx() + .delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var)); + return ControlFlow::Break(reported); + } + }); + } + _ if ty.has_param() || ty.has_bound_vars() => return ty.super_visit_with(self), + _ => {} + } + ControlFlow::Continue(()) + } + + fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result { + match re.kind() { + ty::ReEarlyParam(param) => { + self.params.insert(param.index); + } + ty::ReBound(db, br) if db >= self.depth => { + self.vars.insert(match br.kind { + ty::BrNamed(def_id, name) => (def_id, name), + ty::BrAnon | ty::BrEnv => { + let guar = self + .tcx + .dcx() + .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind)); + return ControlFlow::Break(guar); + } + }); + } + _ => {} + } + ControlFlow::Continue(()) + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + match ct.kind() { + ty::ConstKind::Param(param) => { + self.params.insert(param.index); + } + ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => { + let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var"); + return ControlFlow::Break(guar); + } + _ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self), + _ => {} + } + ControlFlow::Continue(()) + } +} diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 37fbf45235a..68896768e8d 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -408,10 +408,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { traits with associated type `{name}`, you could use the \ fully-qualified path", ), - traits - .iter() - .map(|trait_str| format!("::{name}")) - .collect::>(), + traits.iter().map(|trait_str| format!("::{name}")), Applicability::HasPlaceholders, ); } diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 428eea2f686..42e303c10ea 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -16,7 +16,7 @@ use rustc_middle::ty::{ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt, }; use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, sym}; use smallvec::SmallVec; /// Report an error that a generic argument did not match the generic parameter that was @@ -41,9 +41,11 @@ fn generic_arg_mismatch_err( if let GenericParamDefKind::Const { .. } = param.kind { if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { err.help("const arguments cannot yet be inferred with `_`"); - if sess.is_nightly_build() { - err.help("add `#![feature(generic_arg_infer)]` to the crate attributes to enable"); - } + tcx.disabled_nightly_features( + &mut err, + param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)), + [(String::new(), sym::generic_arg_infer)], + ); } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bf5838143d9..35755a46df3 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -658,10 +658,6 @@ pub fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)), - sym::retag_box_to_raw => { - (2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0))) - } - other => { tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); return; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 22afddad633..4c4ff28808e 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -291,12 +291,16 @@ fn default_body_is_unstable( reason: reason_str, }); + let inject_span = item_did + .as_local() + .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id))); rustc_session::parse::add_feature_diagnostics_for_issue( &mut err, &tcx.sess, feature, rustc_feature::GateIssue::Library(issue), false, + inject_span, ); err.emit(); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f5250045344..41b03f0b66e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -999,9 +999,14 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), // Implments `ConstParamTy`, suggest adding the feature to enable. Ok(..) => true, }; - if may_suggest_feature && tcx.sess.is_nightly_build() { - diag.help( - "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types", + if may_suggest_feature { + tcx.disabled_nightly_features( + &mut diag, + Some(param.hir_id), + [( + " more complex and user defined types".to_string(), + sym::adt_const_params, + )], ); } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 7bf87f444db..fb919714afd 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -295,6 +295,44 @@ pub struct AssocTypeBindingNotAllowed { pub fn_trait_expansion: Option, } +#[derive(Diagnostic)] +#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)] +pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub param_name: Symbol, + pub param_def_kind: &'static str, + pub param_category: &'static str, + #[label(hir_analysis_param_defined_here_label)] + pub param_defined_here_label: Option, + #[subdiagnostic] + pub ty_note: Option>, +} + +#[derive(Subdiagnostic, Clone, Copy)] +#[note(hir_analysis_ty_of_assoc_const_binding_note)] +pub(crate) struct TyOfAssocConstBindingNote<'tcx> { + pub assoc_const: Ident, + pub ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)] +pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub var_name: Symbol, + pub var_def_kind: &'static str, + #[label(hir_analysis_var_defined_here_label)] + pub var_defined_here_label: Span, + #[subdiagnostic] + pub ty_note: Option>, +} + #[derive(Subdiagnostic)] #[help(hir_analysis_parenthesized_fn_trait_expansion)] pub struct ParenthesizedFnTraitExpansion { diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 25d34531379..9a500fa712b 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -755,7 +755,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = callee_expr.kind && let Res::Local(_) = path.res - && let [segment] = &path.segments[..] + && let [segment] = &path.segments { for id in self.tcx.hir().items() { if let Some(node) = self.tcx.hir().get_if_local(id.owner_id.into()) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index bdc796aca3a..193ea040899 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1420,15 +1420,13 @@ impl<'tcx> Pick<'tcx> { } _ => {} } - if tcx.sess.is_nightly_build() { - for (candidate, feature) in &self.unstable_candidates { - lint.help(format!( - "add `#![feature({})]` to the crate attributes to enable `{}`", - feature, - tcx.def_path_str(candidate.item.def_id), - )); - } - } + tcx.disabled_nightly_features( + lint, + Some(scope_expr_id), + self.unstable_candidates.iter().map(|(candidate, feature)| { + (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) + }), + ); }, ); } @@ -1935,7 +1933,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - /// Determine if the associated item withe the given DefId matches + /// Determine if the associated item with the given DefId matches /// the desired name via a doc alias. fn matches_by_doc_alias(&self, def_id: DefId) -> bool { let Some(name) = self.method_name else { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index bb963ad7a39..491da7eb2c2 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -83,6 +83,9 @@ struct PatInfo<'tcx, 'a> { binding_mode: BindingMode, top_info: TopInfo<'tcx>, decl_origin: Option>, + + /// The depth of current pattern + current_depth: u32, } impl<'tcx> FnCtxt<'_, 'tcx> { @@ -152,7 +155,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl_origin: Option>, ) { let info = TopInfo { expected, origin_expr, span }; - let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin }; + let pat_info = + PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin, current_depth: 0 }; self.check_pat(pat, expected, pat_info); } @@ -163,7 +167,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { - let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; + let PatInfo { binding_mode: def_bm, top_info: ti, current_depth, .. } = pat_info; + let path_res = match &pat.kind { PatKind::Path(qpath) => Some( self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None), @@ -172,8 +177,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); - let pat_info = - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin: pat_info.decl_origin }; + let pat_info = PatInfo { + binding_mode: def_bm, + top_info: ti, + decl_origin: pat_info.decl_origin, + current_depth: current_depth + 1, + }; let ty = match pat.kind { PatKind::Wild | PatKind::Err(_) => expected, @@ -1046,14 +1055,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; + let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth } = pat_info; let tcx = self.tcx; let on_error = |e| { for pat in subpats { self.check_pat( pat, Ty::new_error(tcx, e), - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth }, ); } }; @@ -1120,7 +1129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat( subpat, field_ty, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin, current_depth }, ); self.tcx.check_stability( @@ -2134,7 +2143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The expected type must be an array or slice, but was neither, so error. _ => { let guar = expected.error_reported().err().unwrap_or_else(|| { - self.error_expected_array_or_slice(span, expected, pat_info.top_info) + self.error_expected_array_or_slice(span, expected, pat_info) }); let err = Ty::new_error(self.tcx, guar); (err, Some(err), err) @@ -2169,7 +2178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, min_len: u64, ) -> (Option>, Ty<'tcx>) { - let len = match len.eval(self.tcx, self.param_env, None) { + let len = match len.eval(self.tcx, self.param_env, span) { Ok(val) => val .try_to_scalar() .and_then(|scalar| scalar.try_to_int().ok()) @@ -2273,8 +2282,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, expected_ty: Ty<'tcx>, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> ErrorGuaranteed { + let PatInfo { top_info: ti, current_depth, .. } = pat_info; + let mut err = struct_span_code_err!( self.dcx(), span, @@ -2292,9 +2303,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(_) = ti.origin_expr && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let ty = self.resolve_vars_if_possible(ti.expected); - let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(ty); - match is_slice_or_array_or_vector.1.kind() { + let resolved_ty = self.resolve_vars_if_possible(ti.expected); + let (is_slice_or_array_or_vector, resolved_ty) = + self.is_slice_or_array_or_vector(resolved_ty); + match resolved_ty.kind() { ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => @@ -2309,7 +2321,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => (), } - if is_slice_or_array_or_vector.0 { + + let is_top_level = current_depth <= 1; + if is_slice_or_array_or_vector && is_top_level { err.span_suggestion( span, "consider slicing here", diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 89e4e88b3df..44e14387c5c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1475,7 +1475,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, unevaluated: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, - span: Option, + span: Span, ) -> Result, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { Ok(Some(val)) => Ok(ty::Const::new_value(self.tcx, val, ty)), @@ -1509,7 +1509,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, mut param_env: ty::ParamEnv<'tcx>, unevaluated: ty::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { let mut args = self.resolve_vars_if_possible(unevaluated.args); debug!(?args); @@ -1521,12 +1521,9 @@ impl<'tcx> InferCtxt<'tcx> { if let Some(ct) = tcx.thir_abstract_const(unevaluated.def)? { let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, args)); if let Err(e) = ct.error_reported() { - return Err(ErrorHandled::Reported( - e.into(), - span.unwrap_or(rustc_span::DUMMY_SP), - )); + return Err(ErrorHandled::Reported(e.into(), span)); } else if ct.has_non_region_infer() || ct.has_non_region_param() { - return Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))); + return Err(ErrorHandled::TooGeneric(span)); } else { args = replace_param_and_infer_args_with_placeholder(tcx, args); } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 029bddda1e1..3c2071be04e 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -18,7 +18,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extend_one)] diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index a0be1c09c9a..e2010ab3830 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -5,49 +5,11 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{add_elided_lifetime_in_path_suggestion, Diag}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_middle::middle::stability; -use rustc_session::config::ExpectedValues; use rustc_session::lint::BuiltinLintDiag; use rustc_session::Session; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::BytePos; -const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; - -fn check_cfg_expected_note( - sess: &Session, - possibilities: &[Symbol], - type_: &str, - name: Option, - suffix: &str, -) -> String { - use std::fmt::Write; - - let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { - possibilities.len() - } else { - std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) - }; - - let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); - possibilities.sort(); - - let and_more = possibilities.len().saturating_sub(n_possibilities); - let possibilities = possibilities[..n_possibilities].join("`, `"); - - let mut note = String::with_capacity(50 + possibilities.len()); - - write!(&mut note, "expected {type_}").unwrap(); - if let Some(name) = name { - write!(&mut note, " for `{name}`").unwrap(); - } - write!(&mut note, " are: {suffix}`{possibilities}`").unwrap(); - if and_more > 0 { - write!(&mut note, " and {and_more} more").unwrap(); - } - - note -} +mod check_cfg; pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { match diagnostic { @@ -219,242 +181,11 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di diag.help(help); diag.note("see the asm section of Rust By Example for more information"); } - BuiltinLintDiag::UnexpectedCfgName((name, name_span), value) => { - #[allow(rustc::potential_query_instability)] - let possibilities: Vec = - sess.psess.check_config.expecteds.keys().copied().collect(); - - let mut names_possibilities: Vec<_> = if value.is_none() { - // We later sort and display all the possibilities, so the order here does not matter. - #[allow(rustc::potential_query_instability)] - sess.psess - .check_config - .expecteds - .iter() - .filter_map(|(k, v)| match v { - ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), - _ => None, - }) - .collect() - } else { - Vec::new() - }; - - let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - let mut is_feature_cfg = name == sym::feature; - - if is_feature_cfg && is_from_cargo { - diag.help("consider defining some features in `Cargo.toml`"); - // Suggest the most probable if we found one - } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { - if let Some(ExpectedValues::Some(best_match_values)) = - sess.psess.check_config.expecteds.get(&best_match) - { - // We will soon sort, so the initial order does not matter. - #[allow(rustc::potential_query_instability)] - let mut possibilities = - best_match_values.iter().flatten().map(Symbol::as_str).collect::>(); - possibilities.sort(); - - let mut should_print_possibilities = true; - if let Some((value, value_span)) = value { - if best_match_values.contains(&Some(value)) { - diag.span_suggestion( - name_span, - "there is a config with a similar name and value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; - } else if best_match_values.contains(&None) { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and no value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; - } else if let Some(first_value) = possibilities.first() { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - format!("{best_match} = \"{first_value}\""), - Applicability::MaybeIncorrect, - ); - } else { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - best_match, - Applicability::MaybeIncorrect, - ); - }; - } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - if !possibilities.is_empty() && should_print_possibilities { - let possibilities = possibilities.join("`, `"); - diag.help(format!( - "expected values for `{best_match}` are: `{possibilities}`" - )); - } - } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - is_feature_cfg |= best_match == sym::feature; - } else { - if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { - names_possibilities.sort(); - for cfg_name in names_possibilities.iter() { - diag.span_suggestion( - name_span, - "found config with similar value", - format!("{cfg_name} = \"{name}\""), - Applicability::MaybeIncorrect, - ); - } - } - if !possibilities.is_empty() { - diag.help_once(check_cfg_expected_note( - sess, - &possibilities, - "names", - None, - "", - )); - } - } - - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") - } else { - format!("cfg({name})") - }; - - if is_from_cargo { - if !is_feature_cfg { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); - } else { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - diag.note("see for more information about checking conditional configuration"); - } + BuiltinLintDiag::UnexpectedCfgName(name, value) => { + check_cfg::unexpected_cfg_name(sess, diag, name, value) } - BuiltinLintDiag::UnexpectedCfgValue((name, name_span), value) => { - let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) - else { - bug!( - "it shouldn't be possible to have a diagnostic on a value whose name is not in values" - ); - }; - let mut have_none_possibility = false; - // We later sort possibilities if it is not empty, so the - // order here does not matter. - #[allow(rustc::potential_query_instability)] - let possibilities: Vec = values - .iter() - .inspect(|a| have_none_possibility |= a.is_none()) - .copied() - .flatten() - .collect(); - let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - - // Show the full list if all possible values for a given name, but don't do it - // for names as the possibilities could be very long - if !possibilities.is_empty() { - diag.note(check_cfg_expected_note( - sess, - &possibilities, - "values", - Some(name), - if have_none_possibility { "(none), " } else { "" }, - )); - - if let Some((value, value_span)) = value { - // Suggest the most probable if we found one - if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) - { - diag.span_suggestion( - value_span, - "there is a expected value with a similar name", - format!("\"{best_match}\""), - Applicability::MaybeIncorrect, - ); - } - } else if let &[first_possibility] = &possibilities[..] { - diag.span_suggestion( - name_span.shrink_to_hi(), - "specify a config value", - format!(" = \"{first_possibility}\""), - Applicability::MaybeIncorrect, - ); - } - } else if have_none_possibility { - diag.note(format!("no expected value for `{name}`")); - if let Some((_value, value_span)) = value { - diag.span_suggestion( - name_span.shrink_to_hi().to(value_span), - "remove the value", - "", - Applicability::MaybeIncorrect, - ); - } - } else { - diag.note(format!("no expected values for `{name}`")); - - let sp = if let Some((_value, value_span)) = value { - name_span.to(value_span) - } else { - name_span - }; - diag.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); - } - - // We don't want to suggest adding values to well known names - // since those are defined by rustc it-self. Users can still - // do it if they want, but should not encourage them. - let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); - - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") - } else { - format!("cfg({name})") - }; - - if is_from_cargo { - if name == sym::feature { - if let Some((value, _value_span)) = value { - diag.help(format!( - "consider adding `{value}` as a feature in `Cargo.toml`" - )); - } else { - diag.help("consider defining some features in `Cargo.toml`"); - } - } else if !is_cfg_a_well_know_name { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); - } else { - if !is_cfg_a_well_know_name { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - } - diag.note("see for more information about checking conditional configuration"); - } + BuiltinLintDiag::UnexpectedCfgValue(name, value) => { + check_cfg::unexpected_cfg_value(sess, diag, name, value) } BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg) => { let left_sp = diag.span.primary_span().unwrap(); diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs new file mode 100644 index 00000000000..2c9a3a6d1b2 --- /dev/null +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -0,0 +1,277 @@ +use rustc_errors::{Applicability, Diag}; +use rustc_session::{config::ExpectedValues, Session}; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::{sym, Span, Symbol}; + +const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; + +fn check_cfg_expected_note( + sess: &Session, + possibilities: &[Symbol], + type_: &str, + name: Option, + suffix: &str, +) -> String { + use std::fmt::Write; + + let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { + possibilities.len() + } else { + std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) + }; + + let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); + possibilities.sort(); + + let and_more = possibilities.len().saturating_sub(n_possibilities); + let possibilities = possibilities[..n_possibilities].join("`, `"); + + let mut note = String::with_capacity(50 + possibilities.len()); + + write!(&mut note, "expected {type_}").unwrap(); + if let Some(name) = name { + write!(&mut note, " for `{name}`").unwrap(); + } + write!(&mut note, " are: {suffix}`{possibilities}`").unwrap(); + if and_more > 0 { + write!(&mut note, " and {and_more} more").unwrap(); + } + + note +} + +pub(super) fn unexpected_cfg_name( + sess: &Session, + diag: &mut Diag<'_, ()>, + (name, name_span): (Symbol, Span), + value: Option<(Symbol, Span)>, +) { + #[allow(rustc::potential_query_instability)] + let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); + + let mut names_possibilities: Vec<_> = if value.is_none() { + // We later sort and display all the possibilities, so the order here does not matter. + #[allow(rustc::potential_query_instability)] + sess.psess + .check_config + .expecteds + .iter() + .filter_map(|(k, v)| match v { + ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), + _ => None, + }) + .collect() + } else { + Vec::new() + }; + + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + let mut is_feature_cfg = name == sym::feature; + + if is_feature_cfg && is_from_cargo { + diag.help("consider defining some features in `Cargo.toml`"); + // Suggest the most probable if we found one + } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { + if let Some(ExpectedValues::Some(best_match_values)) = + sess.psess.check_config.expecteds.get(&best_match) + { + // We will soon sort, so the initial order does not matter. + #[allow(rustc::potential_query_instability)] + let mut possibilities = + best_match_values.iter().flatten().map(Symbol::as_str).collect::>(); + possibilities.sort(); + + let mut should_print_possibilities = true; + if let Some((value, value_span)) = value { + if best_match_values.contains(&Some(value)) { + diag.span_suggestion( + name_span, + "there is a config with a similar name and value", + best_match, + Applicability::MaybeIncorrect, + ); + should_print_possibilities = false; + } else if best_match_values.contains(&None) { + diag.span_suggestion( + name_span.to(value_span), + "there is a config with a similar name and no value", + best_match, + Applicability::MaybeIncorrect, + ); + should_print_possibilities = false; + } else if let Some(first_value) = possibilities.first() { + diag.span_suggestion( + name_span.to(value_span), + "there is a config with a similar name and different values", + format!("{best_match} = \"{first_value}\""), + Applicability::MaybeIncorrect, + ); + } else { + diag.span_suggestion( + name_span.to(value_span), + "there is a config with a similar name and different values", + best_match, + Applicability::MaybeIncorrect, + ); + }; + } else { + diag.span_suggestion( + name_span, + "there is a config with a similar name", + best_match, + Applicability::MaybeIncorrect, + ); + } + + if !possibilities.is_empty() && should_print_possibilities { + let possibilities = possibilities.join("`, `"); + diag.help(format!("expected values for `{best_match}` are: `{possibilities}`")); + } + } else { + diag.span_suggestion( + name_span, + "there is a config with a similar name", + best_match, + Applicability::MaybeIncorrect, + ); + } + + is_feature_cfg |= best_match == sym::feature; + } else { + if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { + names_possibilities.sort(); + for cfg_name in names_possibilities.iter() { + diag.span_suggestion( + name_span, + "found config with similar value", + format!("{cfg_name} = \"{name}\""), + Applicability::MaybeIncorrect, + ); + } + } + if !possibilities.is_empty() { + diag.help_once(check_cfg_expected_note(sess, &possibilities, "names", None, "")); + } + } + + let inst = if let Some((value, _value_span)) = value { + let pre = if is_from_cargo { "\\" } else { "" }; + format!("cfg({name}, values({pre}\"{value}{pre}\"))") + } else { + format!("cfg({name})") + }; + + if is_from_cargo { + if !is_feature_cfg { + diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); + } + diag.note("see for more information about checking conditional configuration"); + } else { + diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); + diag.note("see for more information about checking conditional configuration"); + } +} + +pub(super) fn unexpected_cfg_value( + sess: &Session, + diag: &mut Diag<'_, ()>, + (name, name_span): (Symbol, Span), + value: Option<(Symbol, Span)>, +) { + let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { + bug!( + "it shouldn't be possible to have a diagnostic on a value whose name is not in values" + ); + }; + let mut have_none_possibility = false; + // We later sort possibilities if it is not empty, so the + // order here does not matter. + #[allow(rustc::potential_query_instability)] + let possibilities: Vec = values + .iter() + .inspect(|a| have_none_possibility |= a.is_none()) + .copied() + .flatten() + .collect(); + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + + // Show the full list if all possible values for a given name, but don't do it + // for names as the possibilities could be very long + if !possibilities.is_empty() { + diag.note(check_cfg_expected_note( + sess, + &possibilities, + "values", + Some(name), + if have_none_possibility { "(none), " } else { "" }, + )); + + if let Some((value, value_span)) = value { + // Suggest the most probable if we found one + if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { + diag.span_suggestion( + value_span, + "there is a expected value with a similar name", + format!("\"{best_match}\""), + Applicability::MaybeIncorrect, + ); + } + } else if let &[first_possibility] = &possibilities[..] { + diag.span_suggestion( + name_span.shrink_to_hi(), + "specify a config value", + format!(" = \"{first_possibility}\""), + Applicability::MaybeIncorrect, + ); + } + } else if have_none_possibility { + diag.note(format!("no expected value for `{name}`")); + if let Some((_value, value_span)) = value { + diag.span_suggestion( + name_span.shrink_to_hi().to(value_span), + "remove the value", + "", + Applicability::MaybeIncorrect, + ); + } + } else { + diag.note(format!("no expected values for `{name}`")); + + let sp = if let Some((_value, value_span)) = value { + name_span.to(value_span) + } else { + name_span + }; + diag.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); + } + + // We don't want to suggest adding values to well known names + // since those are defined by rustc it-self. Users can still + // do it if they want, but should not encourage them. + let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); + + let inst = if let Some((value, _value_span)) = value { + let pre = if is_from_cargo { "\\" } else { "" }; + format!("cfg({name}, values({pre}\"{value}{pre}\"))") + } else { + format!("cfg({name})") + }; + + if is_from_cargo { + if name == sym::feature { + if let Some((value, _value_span)) = value { + diag.help(format!("consider adding `{value}` as a feature in `Cargo.toml`")); + } else { + diag.help("consider defining some features in `Cargo.toml`"); + } + } else if !is_cfg_a_well_know_name { + diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); + } + diag.note("see for more information about checking conditional configuration"); + } else { + if !is_cfg_a_well_know_name { + diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); + } + diag.note("see for more information about checking conditional configuration"); + } +} diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 95f312a31b3..21af6a182a9 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1078,6 +1078,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { feature, GateIssue::Language, lint_from_cli, + None, ); }, ); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index f6253068eaa..067374c0261 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -24,9 +24,7 @@ #include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" -#if LLVM_VERSION_GE(17, 0) #include "llvm/Support/VirtualFileSystem.h" -#endif #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/FunctionImport.h" @@ -334,14 +332,8 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, std::ostringstream Buf; -#if LLVM_VERSION_GE(17, 0) const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const ArrayRef CPUTable = MCInfo->getAllProcessorDescriptions(); -#else - Buf << "Full target CPU help is not supported by this LLVM version.\n\n"; - SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} }; - const ArrayRef CPUTable = TargetCPUKV; -#endif unsigned MaxCPULen = getLongestEntryLength(CPUTable); Buf << "Available CPUs for this target:\n"; @@ -476,10 +468,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.RelaxELFRelocations = RelaxELFRelocations; #endif Options.UseInitArray = UseInitArray; - -#if LLVM_VERSION_LT(17, 0) - Options.ExplicitEmulatedTLS = true; -#endif Options.EmulatedTLS = UseEmulatedTls; if (TrapUnreachable) { @@ -761,16 +749,10 @@ LLVMRustOptimize( } std::optional PGOOpt; -#if LLVM_VERSION_GE(17, 0) auto FS = vfs::getRealFileSystem(); -#endif if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); - PGOOpt = PGOOptions(PGOGenPath, "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, @@ -778,33 +760,21 @@ LLVMRustOptimize( DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); - PGOOpt = PGOOptions(PGOUsePath, "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, #endif DebugInfoForProfiling); } else if (PGOSampleUsePath) { - PGOOpt = PGOOptions(PGOSampleUsePath, "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, #endif DebugInfoForProfiling); } else if (DebugInfoForProfiling) { - PGOOpt = PGOOptions("", "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions("", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, @@ -1353,9 +1323,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, ComputeCrossModuleImport( Ret->Index, Ret->ModuleToDefinedGVSummaries, -#if LLVM_VERSION_GE(17, 0) isPrevailing, -#endif Ret->ImportLists, Ret->ExportLists ); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 1632b9e1249..f6ec410bfac 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -25,6 +25,13 @@ #include +// for raw `write` in the bad-alloc handler +#ifdef _MSC_VER +#include +#else +#include +#endif + //===----------------------------------------------------------------------=== // // This file defines alternate interfaces to core functions that are more @@ -88,8 +95,24 @@ static void FatalErrorHandler(void *UserData, exit(101); } -extern "C" void LLVMRustInstallFatalErrorHandler() { +// Custom error handler for bad-alloc LLVM errors. +// +// It aborts the process without any further allocations, similar to LLVM's +// default except that may be configured to `throw std::bad_alloc()` instead. +static void BadAllocErrorHandler(void *UserData, + const char* Reason, + bool GenCrashDiag) { + const char *OOM = "rustc-LLVM ERROR: out of memory\n"; + (void)!::write(2, OOM, strlen(OOM)); + (void)!::write(2, Reason, strlen(Reason)); + (void)!::write(2, "\n", 1); + abort(); +} + +extern "C" void LLVMRustInstallErrorHandlers() { + install_bad_alloc_error_handler(BadAllocErrorHandler); install_fatal_error_handler(FatalErrorHandler); + install_out_of_memory_new_handler(); } extern "C" void LLVMRustDisableSystemDialogsOnCrash() { @@ -2129,19 +2152,3 @@ extern "C" LLVMValueRef LLVMConstStringInContext2(LLVMContextRef C, return wrap(ConstantDataArray::getString(*unwrap(C), StringRef(Str, Length), !DontNullTerminate)); } #endif - -// FIXME: Remove when Rust's minimum supported LLVM version reaches 17. -// https://github.com/llvm/llvm-project/commit/35276f16e5a2cae0dfb49c0fbf874d4d2f177acc -#if LLVM_VERSION_LT(17, 0) -extern "C" LLVMValueRef LLVMConstArray2(LLVMTypeRef ElementTy, - LLVMValueRef *ConstantVals, - uint64_t Length) { - ArrayRef V(unwrap(ConstantVals, Length), Length); - return wrap(ConstantArray::get(ArrayType::get(unwrap(ElementTy), Length), V)); -} - -extern "C" LLVMTypeRef LLVMArrayType2(LLVMTypeRef ElementTy, - uint64_t ElementCount) { - return wrap(ArrayType::get(unwrap(ElementTy), ElementCount)); -} -#endif diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 0467cf2969f..03783fa9798 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1590,17 +1590,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - // Translate the virtual `/rustc/$hash` prefix back to a real directory - // that should hold actual sources, where possible. - // - // NOTE: if you update this, you might need to also update bootstrap's code for generating - // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. - let virtual_rust_source_base_dir = [ - filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), - filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), - ]; - let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { + // Translate the virtual `/rustc/$hash` prefix back to a real directory + // that should hold actual sources, where possible. + // + // NOTE: if you update this, you might need to also update bootstrap's code for generating + // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. + let virtual_rust_source_base_dir = [ + filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), + filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), + ]; + debug!( "try_to_translate_virtual_to_real(name={:?}): \ virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}", diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index c3e4a03ad16..e47668b9110 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -48,7 +48,7 @@ #![feature(trusted_len)] #![feature(type_alias_impl_trait)] #![feature(strict_provenance)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(rustc_attrs)] #![feature(control_flow_enum)] #![feature(trait_upcasting)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index d8d6899f057..8d9e0dfd869 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -398,8 +398,22 @@ pub fn lint_level( } } - // Finally, run `decorate`. - decorate(&mut err); + // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly), + // so we need to make sure when we do call `decorate` that the diagnostic is eventually + // emitted or we'll get a `must_produce_diag` ICE. + // + // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors: + // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)` + // or `Error`, then the diagnostic will be emitted regardless of CLI options. + // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by + // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic + // will be emitted if `can_emit_warnings` is true. + let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings(); + + if !skip { + decorate(&mut err); + } + explain_lint_level_source(lint, level, src, &mut err); err.emit() } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 5feee3c3ba1..214297b9869 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use rustc_hir::def_id::DefId; use rustc_session::RemapFileNameExt; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{HasDataLayout, Size}; use crate::mir::interpret::{alloc_range, AllocId, ConstAllocation, ErrorHandled, Scalar}; @@ -273,7 +273,7 @@ impl<'tcx> Const<'tcx> { self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - span: Option, + span: Span, ) -> Result, ErrorHandled> { match self { Const::Ty(c) => { @@ -293,7 +293,7 @@ impl<'tcx> Const<'tcx> { /// Normalizes the constant to a value or an error if possible. #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - match self.eval(tcx, param_env, None) { + match self.eval(tcx, param_env, DUMMY_SP) { Ok(val) => Self::Val(val, self.ty()), Err(ErrorHandled::Reported(guar, _span)) => { Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty())) @@ -313,10 +313,10 @@ impl<'tcx> Const<'tcx> { // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that // are valtree leaves, and *not* on references. (References should return the // pointer here, which valtrees don't represent.) - let val = c.eval(tcx, param_env, None).ok()?; + let val = c.eval(tcx, param_env, DUMMY_SP).ok()?; Some(val.unwrap_leaf().into()) } - _ => self.eval(tcx, param_env, None).ok()?.try_to_scalar(), + _ => self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar(), } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 643b61c1de3..04d6301116e 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -24,7 +24,7 @@ impl<'tcx> TyCtxt<'tcx> { let instance = ty::Instance::new(def_id, args); let cid = GlobalId { instance, promoted: None }; let param_env = self.param_env(def_id).with_reveal_all_normalized(self); - self.const_eval_global_id(param_env, cid, None) + self.const_eval_global_id(param_env, cid, DUMMY_SP) } /// Resolves and evaluates a constant. /// @@ -40,7 +40,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, ct: mir::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToConstValueResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve` @@ -73,7 +73,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve` @@ -130,7 +130,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, - span: Option, + span: Span, ) -> EvalToConstValueResult<'tcx> { self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) } @@ -141,12 +141,12 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, - span: Option, + span: Span, ) -> EvalToConstValueResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid)); - if let Some(span) = span { + if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span)) } else { @@ -160,13 +160,13 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid)); debug!(?inputs); - if let Some(span) = span { + if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span)) } else { diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index dc4cd203415..6dcea2aaff1 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -164,6 +164,19 @@ pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, + pub normalization_nested_goals: NestedNormalizationGoals<'tcx>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] +pub struct NestedNormalizationGoals<'tcx>(pub Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>); +impl<'tcx> NestedNormalizationGoals<'tcx> { + pub fn empty() -> Self { + NestedNormalizationGoals(vec![]) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } } // FIXME: Having to clone `region_constraints` for folding feels bad and @@ -183,6 +196,10 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { .iter() .map(|opaque| opaque.try_fold_with(folder)) .collect::>()?, + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .try_fold_with(folder)?, })) } @@ -190,6 +207,7 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().fold_with(folder), opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), + normalization_nested_goals: self.normalization_nested_goals.clone().fold_with(folder), }) } } @@ -197,7 +215,8 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { impl<'tcx> TypeVisitable> for ExternalConstraints<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { try_visit!(self.region_constraints.visit_with(visitor)); - self.opaque_types.visit_with(visitor) + try_visit!(self.opaque_types.visit_with(visitor)); + self.normalization_nested_goals.visit_with(visitor) } } @@ -239,7 +258,7 @@ impl<'tcx> TypeVisitable> for PredefinedOpaques<'tcx> { /// /// This is necessary as we treat nested goals different depending on /// their source. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeVisitable, TypeFoldable)] pub enum GoalSource { Misc, /// We're proving a where-bound of an impl. @@ -256,12 +275,6 @@ pub enum GoalSource { ImplWhereBound, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] -pub enum IsNormalizesToHack { - Yes, - No, -} - /// Possible ways the given goal can be proven. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CandidateSource { diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 90180af3b16..16842c8208f 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -19,8 +19,8 @@ //! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{ - CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, - NoSolution, QueryInput, QueryResult, + CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, NoSolution, + QueryInput, QueryResult, }; use crate::{infer::canonical::CanonicalVarValues, ty}; use format::ProofTreeFormatter; @@ -50,7 +50,7 @@ pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>; #[derive(Eq, PartialEq)] pub enum GoalEvaluationKind<'tcx> { Root { orig_values: Vec> }, - Nested { is_normalizes_to_hack: IsNormalizesToHack }, + Nested, } #[derive(Eq, PartialEq)] diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 8d3109a7b28..43931205017 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -55,10 +55,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result { let goal_text = match eval.kind { GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL", - GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack { - IsNormalizesToHack::No => "GOAL", - IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL", - }, + GoalEvaluationKind::Nested => "GOAL", }; write!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?; self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation)) diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5b62c0bf931..0d621cd1cfd 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -335,7 +335,7 @@ impl<'tcx> Const<'tcx> { self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - span: Option, + span: Span, ) -> Result, ErrorHandled> { assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); match self.kind() { @@ -349,7 +349,7 @@ impl<'tcx> Const<'tcx> { else { // This can happen when we run on ill-typed code. let e = tcx.dcx().span_delayed_bug( - span.unwrap_or(DUMMY_SP), + span, "`ty::Const::eval` called on a non-valtree-compatible type", ); return Err(e.into()); @@ -362,14 +362,14 @@ impl<'tcx> Const<'tcx> { | ConstKind::Infer(_) | ConstKind::Bound(_, _) | ConstKind::Placeholder(_) - | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span.unwrap_or(DUMMY_SP))), + | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)), } } /// Normalizes the constant to a value or an error if possible. #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - match self.eval(tcx, param_env, None) { + match self.eval(tcx, param_env, DUMMY_SP) { Ok(val) => Self::new_value(tcx, val, self.ty()), Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into(), self.ty()), Err(ErrorHandled::TooGeneric(_span)) => self, @@ -382,7 +382,7 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option { - self.eval(tcx, param_env, None).ok()?.try_to_scalar() + self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar() } #[inline] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 17ba97c5fd3..10a4da40429 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -43,7 +43,9 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal} #[cfg(parallel_compiler)] use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan}; +use rustc_errors::{ + Applicability, Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; @@ -2174,6 +2176,45 @@ impl<'tcx> TyCtxt<'tcx> { lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate); } + /// Find the crate root and the appropriate span where `use` and outer attributes can be + /// inserted at. + pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option { + for (_hir_id, node) in self.hir().parent_iter(hir_id) { + if let hir::Node::Crate(m) = node { + return Some(m.spans.inject_use_span.shrink_to_lo()); + } + } + None + } + + pub fn disabled_nightly_features( + self, + diag: &mut Diag<'_, E>, + hir_id: Option, + features: impl IntoIterator, + ) { + if !self.sess.is_nightly_build() { + return; + } + + let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id)); + for (desc, feature) in features { + // FIXME: make this string translatable + let msg = + format!("add `#![feature({feature})]` to the crate attributes to enable{desc}"); + if let Some(span) = span { + diag.span_suggestion_verbose( + span, + msg, + format!("#![feature({feature})]\n"), + Applicability::MachineApplicable, + ); + } else { + diag.help(msg); + } + } + } + /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically /// generated by `#[derive(LintDiagnostic)]`). #[track_caller] diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 88ac05cabb6..aef63896dde 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -774,7 +774,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (current_root, parent_root) = if self.tcx.sess.opts.unstable_opts.maximal_hir_to_mir_coverage { // Some consumers of rustc need to map MIR locations back to HIR nodes. Currently - // the the only part of rustc that tracks MIR -> HIR is the + // the only part of rustc that tracks MIR -> HIR is the // `SourceScopeLocalData::lint_root` field that tracks lint levels for MIR // locations. Normally the number of source scopes is limited to the set of nodes // with lint annotations. The -Zmaximal-hir-to-mir-coverage flag changes this diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index e3f202b7f18..b9a20cb21e9 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -5,7 +5,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(assert_matches)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(let_chains)] diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 20d3b3f98ce..cfa853417ca 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -938,9 +938,6 @@ fn report_non_exhaustive_match<'p, 'tcx>( }; // In the case of an empty match, replace the '`_` not covered' diagnostic with something more // informative. - let mut err; - let pattern; - let patterns_len; if is_empty_match && !non_empty_enum { return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty { cx, @@ -948,33 +945,23 @@ fn report_non_exhaustive_match<'p, 'tcx>( span: sp, ty: scrut_ty, }); - } else { - // FIXME: migration of this diagnostic will require list support - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: {joined_patterns} not covered"), - ); - err.span_label( - sp, - format!( - "pattern{} {} not covered", - rustc_errors::pluralize!(witnesses.len()), - joined_patterns - ), - ); - patterns_len = witnesses.len(); - pattern = if witnesses.len() < 4 { - witnesses - .iter() - .map(|witness| cx.hoist_witness_pat(witness).to_string()) - .collect::>() - .join(" | ") - } else { - "_".to_string() - }; - }; + } + + // FIXME: migration of this diagnostic will require list support + let joined_patterns = joined_uncovered_patterns(cx, &witnesses); + let mut err = create_e0004( + cx.tcx.sess, + sp, + format!("non-exhaustive patterns: {joined_patterns} not covered"), + ); + err.span_label( + sp, + format!( + "pattern{} {} not covered", + rustc_errors::pluralize!(witnesses.len()), + joined_patterns + ), + ); // Point at the definition of non-covered `enum` variants. if let Some(AdtDefinedHere { adt_def_span, ty, variants }) = @@ -1021,6 +1008,23 @@ fn report_non_exhaustive_match<'p, 'tcx>( } } + // Whether we suggest the actual missing patterns or `_`. + let suggest_the_witnesses = witnesses.len() < 4; + let suggested_arm = if suggest_the_witnesses { + let pattern = witnesses + .iter() + .map(|witness| cx.hoist_witness_pat(witness).to_string()) + .collect::>() + .join(" | "); + if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns { + // Arms with a never pattern don't take a body. + pattern + } else { + format!("{pattern} => todo!()") + } + } else { + format!("_ => todo!()") + }; let mut suggestion = None; let sm = cx.tcx.sess.source_map(); match arms { @@ -1033,7 +1037,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( }; suggestion = Some(( sp.shrink_to_hi().with_hi(expr_span.hi()), - format!(" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",), + format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",), )); } [only] => { @@ -1059,7 +1063,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( }; suggestion = Some(( only.span.shrink_to_hi(), - format!("{comma}{pre_indentation}{pattern} => todo!()"), + format!("{comma}{pre_indentation}{suggested_arm}"), )); } [.., prev, last] => { @@ -1082,7 +1086,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( if let Some(spacing) = spacing { suggestion = Some(( last.span.shrink_to_hi(), - format!("{comma}{spacing}{pattern} => todo!()"), + format!("{comma}{spacing}{suggested_arm}"), )); } } @@ -1093,13 +1097,13 @@ fn report_non_exhaustive_match<'p, 'tcx>( let msg = format!( "ensure that all possible cases are being handled by adding a match arm with a wildcard \ pattern{}{}", - if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() { + if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() { ", a match arm with multiple or-patterns" } else { // we are either not suggesting anything, or suggesting `_` "" }, - match patterns_len { + match witnesses.len() { // non-exhaustive enum case 0 if suggestion.is_some() => " as shown", 0 => "", diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index ac3043afcff..03eda9a9322 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -524,12 +524,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Prefer valtrees over opaque constants. let const_value = self .tcx - .const_eval_global_id_for_typeck(param_env_reveal_all, cid, Some(span)) + .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) .map(|val| match val { Some(valtree) => mir::Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), None => mir::Const::Val( self.tcx - .const_eval_global_id(param_env_reveal_all, cid, Some(span)) + .const_eval_global_id(param_env_reveal_all, cid, span) .expect("const_eval_global_id_for_typeck should have already failed"), ty, ), @@ -627,15 +627,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // First try using a valtree in order to destructure the constant into a pattern. // FIXME: replace "try to do a thing, then fall back to another thing" // but something more principled, like a trait query checking whether this can be turned into a valtree. - if let Ok(Some(valtree)) = - self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span)) + if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) { let subpattern = self.const_to_pat(Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), id, span); PatKind::InlineConstant { subpattern, def: def_id } } else { // If that fails, convert it to an opaque constant pattern. - match tcx.const_eval_resolve(self.param_env, uneval, Some(span)) { + match tcx.const_eval_resolve(self.param_env, uneval, span) { Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind, Err(ErrorHandled::TooGeneric(_)) => { // If we land here it means the const can't be evaluated because it's `TooGeneric`. diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 6f668aa4ce8..f880476cec2 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -24,7 +24,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b | ty::Str | ty::FnDef(..) | ty::Never => false, - // References + // References and Boxes (`noalias` sources) ty::Ref(..) => true, ty::Adt(..) if ty.is_box() => true, ty::Adt(adt, _) if Some(adt.did()) == tcx.lang_items().ptr_unique() => true, @@ -125,15 +125,39 @@ impl<'tcx> MirPass<'tcx> for AddRetag { for i in (0..block_data.statements.len()).rev() { let (retag_kind, place) = match block_data.statements[i].kind { // Retag after assignments of reference type. - StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => { + StatementKind::Assign(box (ref place, ref rvalue)) => { let add_retag = match rvalue { // Ptr-creating operations already do their own internal retagging, no // need to also add a retag statement. - Rvalue::Ref(..) | Rvalue::AddressOf(..) => false, - _ => true, + // *Except* if we are deref'ing a Box, because those get desugared to directly working + // with the inner raw pointer! That's relevant for `AddressOf` as Miri otherwise makes it + // a NOP when the original pointer is already raw. + Rvalue::AddressOf(_mutbl, place) => { + // Using `is_box_global` here is a bit sketchy: if this code is + // generic over the allocator, we'll not add a retag! This is a hack + // to make Stacked Borrows compatible with custom allocator code. + // Long-term, we'll want to move to an aliasing model where "cast to + // raw pointer" is a complete NOP, and then this will no longer be + // an issue. + if place.is_indirect_first_projection() + && body.local_decls[place.local].ty.is_box_global(tcx) + { + Some(RetagKind::Raw) + } else { + None + } + } + Rvalue::Ref(..) => None, + _ => { + if needs_retag(place) { + Some(RetagKind::Default) + } else { + None + } + } }; - if add_retag { - (RetagKind::Default, *place) + if let Some(kind) = add_retag { + (kind, *place) } else { continue; } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 54b13a40e92..0d18d4fd69e 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1964,15 +1964,21 @@ fn check_must_not_suspend_ty<'tcx>( debug!("Checking must_not_suspend for {}", ty); match *ty.kind() { - ty::Adt(..) if ty.is_box() => { - let boxed_ty = ty.boxed_ty(); - let descr_pre = &format!("{}boxed ", data.descr_pre); + ty::Adt(_, args) if ty.is_box() => { + let boxed_ty = args.type_at(0); + let allocator_ty = args.type_at(1); check_must_not_suspend_ty( tcx, boxed_ty, hir_id, param_env, - SuspendCheckData { descr_pre, ..data }, + SuspendCheckData { descr_pre: &format!("{}boxed ", data.descr_pre), ..data }, + ) || check_must_not_suspend_ty( + tcx, + allocator_ty, + hir_id, + param_env, + SuspendCheckData { descr_pre: &format!("{}allocator ", data.descr_pre), ..data }, ) } ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data), diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 86097bdcd95..3f6a4156044 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -401,9 +401,8 @@ pub(super) fn extract_branch_mappings( } let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; - let bcb_from_marker = |marker: BlockMarkerId| { - Some(basic_coverage_blocks.bcb_from_bb(block_markers[marker]?)?) - }; + let bcb_from_marker = + |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index f456196b282..3389305e7ee 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -394,7 +394,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } Operand::Constant(box constant) => { if let Ok(constant) = - self.ecx.eval_mir_constant(&constant.const_, Some(constant.span), None) + self.ecx.eval_mir_constant(&constant.const_, constant.span, None) { self.assign_constant(state, place, constant, &[]); } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a3a2108787a..87dff49e0be 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -367,7 +367,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Repeat(..) => return None, Constant { ref value, disambiguator: _ } => { - self.ecx.eval_mir_constant(value, None, None).ok()? + self.ecx.eval_mir_constant(value, DUMMY_SP, None).ok()? } Aggregate(kind, variant, ref fields) => { let fields = fields diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 6629face940..116d6f48456 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -417,7 +417,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { let constant = - self.ecx.eval_mir_constant(&constant.const_, Some(constant.span), None).ok()?; + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).ok()?; self.process_constant(bb, lhs, constant, state); } // Transfer the conditions on the copied rhs. diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 4bca437ea6f..f19b78a3a5c 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -261,7 +261,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // manually normalized. let val = self.tcx.try_normalize_erasing_regions(self.param_env, c.const_).ok()?; - self.use_ecx(|this| this.ecx.eval_mir_constant(&val, Some(c.span), None))? + self.use_ecx(|this| this.ecx.eval_mir_constant(&val, c.span, None))? .as_mplace_or_imm() .right() } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 45513801522..afe228be127 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -507,7 +507,7 @@ fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ &cleanup_post_borrowck::CleanupPostBorrowck, &remove_noop_landing_pads::RemoveNoopLandingPads, - &simplify::SimplifyCfg::EarlyOpt, + &simplify::SimplifyCfg::PostAnalysis, &deref_separator::Derefer, ]; @@ -529,11 +529,11 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // AddMovesForPackedDrops needs to run after drop // elaboration. &add_moves_for_packed_drops::AddMovesForPackedDrops, - // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, + // `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. Otherwise it should run fairly late, // but before optimizations begin. + &add_retag::AddRetag, &elaborate_box_derefs::ElaborateBoxDerefs, &coroutine::StateTransform, - &add_retag::AddRetag, &Lint(known_panics_lint::KnownPanicsLint), ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); @@ -544,7 +544,7 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ &lower_intrinsics::LowerIntrinsics, &remove_place_mention::RemovePlaceMention, - &simplify::SimplifyCfg::ElaborateDrops, + &simplify::SimplifyCfg::PreOptimizations, ]; pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8c8818bd68e..574330cc355 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -37,8 +37,11 @@ pub enum SimplifyCfg { Initial, PromoteConsts, RemoveFalseEdges, - EarlyOpt, - ElaborateDrops, + /// Runs at the beginning of "analysis to runtime" lowering, *before* drop elaboration. + PostAnalysis, + /// Runs at the end of "analysis to runtime" lowering, *after* drop elaboration. + /// This is before the main optimization passes on runtime MIR kick in. + PreOptimizations, Final, MakeShim, AfterUninhabitedEnumBranching, @@ -50,8 +53,8 @@ impl SimplifyCfg { SimplifyCfg::Initial => "SimplifyCfg-initial", SimplifyCfg::PromoteConsts => "SimplifyCfg-promote-consts", SimplifyCfg::RemoveFalseEdges => "SimplifyCfg-remove-false-edges", - SimplifyCfg::EarlyOpt => "SimplifyCfg-early-opt", - SimplifyCfg::ElaborateDrops => "SimplifyCfg-elaborate-drops", + SimplifyCfg::PostAnalysis => "SimplifyCfg-post-analysis", + SimplifyCfg::PreOptimizations => "SimplifyCfg-pre-optimizations", SimplifyCfg::Final => "SimplifyCfg-final", SimplifyCfg::MakeShim => "SimplifyCfg-make_shim", SimplifyCfg::AfterUninhabitedEnumBranching => { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cd9eb4916ce..d8bdbd8c442 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -828,7 +828,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { // a codegen-time error). rustc stops after collection if there was an error, so this // ensures codegen never has to worry about failing consts. // (codegen relies on this and ICEs will happen if this is violated.) - let val = match const_.eval(self.tcx, param_env, Some(constant.span)) { + let val = match const_.eval(self.tcx, param_env, constant.span) { Ok(v) => v, Err(ErrorHandled::TooGeneric(..)) => span_bug!( self.body.source_info(location).span, @@ -1433,7 +1433,7 @@ fn create_mono_items_for_default_impls<'tcx>( } } -/// Scans the CTFE alloc in order to find function calls, closures, and drop-glue. +/// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { match tcx.global_alloc(alloc_id) { GlobalAlloc::Static(def_id) => { @@ -1446,9 +1446,13 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } GlobalAlloc::Memory(alloc) => { trace!("collecting {:?} with {:#?}", alloc_id, alloc); - for &prov in alloc.inner().provenance().ptrs().values() { - rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_alloc(tcx, prov.alloc_id(), output); + let ptrs = alloc.inner().provenance().ptrs(); + // avoid `ensure_sufficient_stack` in the common case of "no pointers" + if !ptrs.is_empty() { + rustc_data_structures::stack::ensure_sufficient_stack(move || { + for &prov in ptrs.values() { + collect_alloc(tcx, prov.alloc_id(), output); + } }); } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 12879dc429b..13023d6fbbd 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -449,6 +449,7 @@ impl<'a> Parser<'a> { parser } + #[inline] pub fn recovery(mut self, recovery: Recovery) -> Self { self.recovery = recovery; self @@ -461,6 +462,7 @@ impl<'a> Parser<'a> { /// /// Technically, this only needs to restrict eager recovery by doing lookahead at more tokens. /// But making the distinction is very subtle, and simply forbidding all recovery is a lot simpler to uphold. + #[inline] fn may_recover(&self) -> bool { matches!(self.recovery, Recovery::Allowed) } @@ -542,6 +544,7 @@ impl<'a> Parser<'a> { /// /// This method will automatically add `tok` to `expected_tokens` if `tok` is not /// encountered. + #[inline] fn check(&mut self, tok: &TokenKind) -> bool { let is_present = self.token == *tok; if !is_present { @@ -550,6 +553,7 @@ impl<'a> Parser<'a> { is_present } + #[inline] fn check_noexpect(&self, tok: &TokenKind) -> bool { self.token == *tok } @@ -558,6 +562,7 @@ impl<'a> Parser<'a> { /// /// the main purpose of this function is to reduce the cluttering of the suggestions list /// which using the normal eat method could introduce in some cases. + #[inline] pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { @@ -567,6 +572,7 @@ impl<'a> Parser<'a> { } /// Consumes a token 'tok' if it exists. Returns whether the given token was present. + #[inline] pub fn eat(&mut self, tok: &TokenKind) -> bool { let is_present = self.check(tok); if is_present { @@ -577,11 +583,13 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, returns `true` without eating it. /// An expectation is also added for diagnostics purposes. + #[inline] fn check_keyword(&mut self, kw: Symbol) -> bool { self.expected_tokens.push(TokenType::Keyword(kw)); self.token.is_keyword(kw) } + #[inline] fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; @@ -600,6 +608,7 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. + #[inline] pub fn eat_keyword(&mut self, kw: Symbol) -> bool { if self.check_keyword(kw) { self.bump(); @@ -612,6 +621,7 @@ impl<'a> Parser<'a> { /// Eats a keyword, optionally ignoring the case. /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. + #[inline] fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; @@ -629,6 +639,7 @@ impl<'a> Parser<'a> { false } + #[inline] fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -650,6 +661,7 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } + #[inline] fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool { if ok { true @@ -697,6 +709,7 @@ impl<'a> Parser<'a> { /// Checks to see if the next token is either `+` or `+=`. /// Otherwise returns `false`. + #[inline] fn check_plus(&mut self) -> bool { self.check_or_expected( self.token.is_like_plus(), diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 163d10d03f0..a9fcc134c13 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -731,8 +731,6 @@ impl<'a> Parser<'a> { && matches!(args.output, ast::FnRetTy::Default(..)) { self.psess.gated_spans.gate(sym::return_type_notation, span); - } else { - self.psess.gated_spans.gate(sym::associated_type_bounds, span); } } let constraint = diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 30a65d69c4e..8080216a252 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -155,16 +155,11 @@ impl<'tcx> CheckConstVisitor<'tcx> { // // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This // is a pretty narrow case, however. - if tcx.sess.is_nightly_build() { - for gate in missing_secondary { - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - err.help(format!( - "add `#![feature({gate})]` to the crate attributes to enable" - )); - } - } + tcx.disabled_nightly_features( + &mut err, + def_id.map(|id| tcx.local_def_id_to_hir_id(id)), + missing_secondary.into_iter().map(|gate| (String::new(), *gate)), + ); err.emit(); } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 2af5a54a0ca..f34e8d96f05 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -7,7 +7,6 @@ use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{sigpipe, CrateType, EntryFnType}; -use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -133,16 +132,6 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, return None; } - if main_def.is_import && !tcx.features().imported_main { - let span = main_def.span; - feature_err( - &tcx.sess, - sym::imported_main, - span, - "using an imported function as entry point `main` is experimental", - ) - .emit(); - } return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })); } no_main_err(tcx, visitor); diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index bbb89576ef7..5b58d7f43b2 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -678,15 +678,19 @@ pub enum Constructor { Or, /// Wildcard pattern. Wildcard, + /// Never pattern. Only used in `WitnessPat`. An actual never pattern should be lowered as + /// `Wildcard`. + Never, /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used - /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. + /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. Only + /// used in `WitnessPat`. NonExhaustive, - /// Fake extra constructor for variants that should not be mentioned in diagnostics. - /// We use this for variants behind an unstable gate as well as - /// `#[doc(hidden)]` ones. + /// Fake extra constructor for variants that should not be mentioned in diagnostics. We use this + /// for variants behind an unstable gate as well as `#[doc(hidden)]` ones. Only used in + /// `WitnessPat`. Hidden, /// Fake extra constructor for constructors that are not seen in the matrix, as explained at the - /// top of the file. + /// top of the file. Only used for specialization. Missing, /// Fake extra constructor that indicates and empty field that is private. When we encounter one /// we skip the column entirely so we don't observe its emptiness. Only used for specialization. @@ -708,6 +712,7 @@ impl Clone for Constructor { Constructor::Str(value) => Constructor::Str(value.clone()), Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()), Constructor::Or => Constructor::Or, + Constructor::Never => Constructor::Never, Constructor::Wildcard => Constructor::Wildcard, Constructor::NonExhaustive => Constructor::NonExhaustive, Constructor::Hidden => Constructor::Hidden, @@ -1040,10 +1045,32 @@ impl ConstructorSet { // In a `MaybeInvalid` place even an empty pattern may be reachable. We therefore // add a dummy empty constructor here, which will be ignored if the place is // `ValidOnly`. - missing_empty.push(NonExhaustive); + missing_empty.push(Never); } } SplitConstructorSet { present, missing, missing_empty } } + + /// Whether this set only contains empty constructors. + pub(crate) fn all_empty(&self) -> bool { + match self { + ConstructorSet::Bool + | ConstructorSet::Integers { .. } + | ConstructorSet::Ref + | ConstructorSet::Union + | ConstructorSet::Unlistable => false, + ConstructorSet::NoConstructors => true, + ConstructorSet::Struct { empty } => *empty, + ConstructorSet::Variants { variants, non_exhaustive } => { + !*non_exhaustive + && variants + .iter() + .all(|visibility| matches!(visibility, VariantVisibility::Empty)) + } + ConstructorSet::Slice { array_len, subtype_is_empty } => { + *subtype_is_empty && matches!(array_len, Some(1..)) + } + } + } } diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index e3667d44bc9..e7eed673c94 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -208,6 +208,7 @@ impl fmt::Debug for DeconstructedPat { } Ok(()) } + Never => write!(f, "!"), Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { write!(f, "_ : {:?}", pat.ty()) } @@ -311,18 +312,24 @@ impl WitnessPat { pub(crate) fn new(ctor: Constructor, fields: Vec, ty: Cx::Ty) -> Self { Self { ctor, fields, ty } } - pub(crate) fn wildcard(ty: Cx::Ty) -> Self { - Self::new(Wildcard, Vec::new(), ty) + /// Create a wildcard pattern for this type. If the type is empty, we create a `!` pattern. + pub(crate) fn wildcard(cx: &Cx, ty: Cx::Ty) -> Self { + let is_empty = cx.ctors_for_ty(&ty).is_ok_and(|ctors| ctors.all_empty()); + let ctor = if is_empty { Never } else { Wildcard }; + Self::new(ctor, Vec::new(), ty) } /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor, ty: Cx::Ty) -> Self { + if matches!(ctor, Wildcard) { + return Self::wildcard(cx, ty); + } let fields = cx .ctor_sub_tys(&ctor, &ty) .filter(|(_, PrivateUninhabitedField(skip))| !skip) - .map(|(ty, _)| Self::wildcard(ty)) + .map(|(ty, _)| Self::wildcard(cx, ty)) .collect(); Self::new(ctor, fields, ty) } @@ -334,6 +341,14 @@ impl WitnessPat { &self.ty } + pub fn is_never_pattern(&self) -> bool { + match self.ctor() { + Never => true, + Or => self.fields.iter().all(|p| p.is_never_pattern()), + _ => self.fields.iter().any(|p| p.is_never_pattern()), + } + } + pub fn iter_fields(&self) -> impl Iterator> { self.fields.iter() } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index fd51fbedeef..eedc00a5613 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -247,7 +247,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) - | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[], + | Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[], Or => { bug!("called `Fields::wildcards` on an `Or` ctor") } @@ -275,7 +275,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { Ref => 1, Slice(slice) => slice.arity(), Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) - | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0, + | Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0, Or => bug!("The `Or` constructor doesn't have a fixed arity"), } } @@ -824,7 +824,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } &Str(value) => PatKind::Constant { value }, - Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, + Never if self.tcx.features().never_patterns => PatKind::Never, + Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index bb822c611a1..b67b7d79d97 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -8,7 +8,7 @@ #![doc(rust_logo)] #![allow(internal_features)] #![feature(rustdoc_internals)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(const_option)] #![feature(core_intrinsics)] #![feature(generic_nonzero)] diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 42c681e4961..179fd79bef7 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -24,6 +24,9 @@ session_feature_diagnostic_for_issue = session_feature_diagnostic_help = add `#![feature({$feature})]` to the crate attributes to enable +session_feature_diagnostic_suggestion = + add `#![feature({$feature})]` to the crate attributes to enable + session_feature_suggest_upgrade_compiler = this compiler was built on {$date}; consider upgrading it if it is out of date diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b7ee2c98025..e56684808bb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -144,18 +144,12 @@ pub enum InstrumentCoverage { } /// Individual flag values controlled by `-Z coverage-options`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct CoverageOptions { /// Add branch coverage instrumentation. pub branch: bool, } -impl Default for CoverageOptions { - fn default() -> Self { - Self { branch: false } - } -} - /// Settings for `-Z instrument-xray` flag. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct InstrumentXRay { diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index d523da1ad7e..cfbeac79e50 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -54,6 +54,18 @@ pub struct FeatureDiagnosticHelp { pub feature: Symbol, } +#[derive(Subdiagnostic)] +#[suggestion( + session_feature_diagnostic_suggestion, + applicability = "maybe-incorrect", + code = "#![feature({feature})]\n" +)] +pub struct FeatureDiagnosticSuggestion { + pub feature: Symbol, + #[primary_span] + pub span: Span, +} + #[derive(Subdiagnostic)] #[help(session_cli_feature_diagnostic_help)] pub struct CliFeatureDiagnosticHelp { diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 398138d7e1f..5434bbe0b98 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -3,8 +3,8 @@ use crate::config::{Cfg, CheckCfg}; use crate::errors::{ - CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError, - SuggestUpgradeCompiler, + CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, + FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler, }; use crate::lint::{ builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiag, Lint, LintId, @@ -112,7 +112,7 @@ pub fn feature_err_issue( } let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() }); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); err } @@ -141,7 +141,7 @@ pub fn feature_warn_issue( explain: &'static str, ) { let mut err = sess.psess.dcx.struct_span_warn(span, explain); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; @@ -160,7 +160,7 @@ pub fn add_feature_diagnostics( sess: &Session, feature: Symbol, ) { - add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false); + add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None); } /// Adds the diagnostics for a feature to an existing error. @@ -175,6 +175,7 @@ pub fn add_feature_diagnostics_for_issue( feature: Symbol, issue: GateIssue, feature_from_cli: bool, + inject_span: Option, ) { if let Some(n) = find_feature_issue(feature, issue) { err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n }); @@ -184,6 +185,8 @@ pub fn add_feature_diagnostics_for_issue( if sess.psess.unstable_features.is_nightly_build() { if feature_from_cli { err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature }); + } else if let Some(span) = inject_span { + err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span }); } else { err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature }); } diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index a13262cdcc4..0762016ef75 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -56,7 +56,7 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { let const_ = self.monomorphize(constant.const_); - let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) { + let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) { Ok(v) => v, Err(mir::interpret::ErrorHandled::Reported(..)) => return, Err(mir::interpret::ErrorHandled::TooGeneric(..)) => { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 509f0def256..d427e5fb88d 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -566,7 +566,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let result = tcx.const_eval_instance( ParamEnv::reveal_all(), instance, - Some(tcx.def_span(instance.def_id())), + tcx.def_span(instance.def_id()), ); result .map(|const_val| { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e43c9533382..63b950a2803 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1463,7 +1463,6 @@ symbols! { residual, result, resume, - retag_box_to_raw, return_position_impl_trait_in_trait, return_type_notation, rhs, diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index fd3d62d1321..9fac0ea7715 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -17,7 +17,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(assert_matches)] -#![feature(associated_type_bounds)] +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(control_flow_enum)] diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 67657c81cf6..e081a9100e2 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -21,8 +21,9 @@ //! However, if `?fresh_var` ends up geteting equated to another type, we retry the //! `NormalizesTo` goal, at which point the opaque is actually defined. -use super::{EvalCtxt, GoalSource}; +use super::EvalCtxt; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::GoalSource; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty::{self, Ty}; @@ -121,10 +122,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::TermKind::Const(_) => { if let Some(alias) = term.to_alias_ty(self.tcx()) { let term = self.next_term_infer_of_kind(term); - self.add_goal( - GoalSource::Misc, - Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }), - ); + self.add_normalizes_to_goal(Goal::new( + self.tcx(), + param_env, + ty::NormalizesTo { alias, term }, + )); self.try_evaluate_added_goals()?; Ok(Some(self.resolve_vars_if_possible(term))) } else { @@ -145,18 +147,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return None; } - let ty::Alias(_, alias) = *ty.kind() else { + let ty::Alias(kind, alias) = *ty.kind() else { return Some(ty); }; match self.commit_if_ok(|this| { + let tcx = this.tcx(); let normalized_ty = this.next_ty_infer(); - let normalizes_to_goal = Goal::new( - this.tcx(), - param_env, - ty::NormalizesTo { alias, term: normalized_ty.into() }, - ); - this.add_goal(GoalSource::Misc, normalizes_to_goal); + let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() }; + match kind { + ty::AliasKind::Opaque => { + // HACK: Unlike for associated types, `normalizes-to` for opaques + // is currently not treated as a function. We do not erase the + // expected term. + this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to)); + } + ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => { + this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to)) + } + } this.try_evaluate_added_goals()?; Ok(this.resolve_vars_if_possible(normalized_ty)) }) { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 251b0a193f1..619435d2e8d 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -9,6 +9,7 @@ //! //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; +use crate::solve::eval_ctxt::NestedGoals; use crate::solve::{ inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response, }; @@ -19,6 +20,7 @@ use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_infer::infer::resolve::EagerResolver; use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::traits::solve::NestedNormalizationGoals; use rustc_middle::infer::canonical::Canonical; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ @@ -28,6 +30,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer}; use rustc_span::DUMMY_SP; +use std::assert_matches::assert_matches; use std::iter; use std::ops::Deref; @@ -93,13 +96,31 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { previous call to `try_evaluate_added_goals!`" ); - let certainty = certainty.unify_with(goals_certainty); - - let var_values = self.var_values; - let external_constraints = self.compute_external_query_constraints()?; + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. + // + // As we return all ambiguous nested goals, we can ignore the certainty returned + // by `try_evaluate_added_goals()`. + let (certainty, normalization_nested_goals) = if self.is_normalizes_to_goal { + let NestedGoals { normalizes_to_goals, goals } = std::mem::take(&mut self.nested_goals); + if cfg!(debug_assertions) { + assert!(normalizes_to_goals.is_empty()); + if goals.is_empty() { + assert_matches!(goals_certainty, Certainty::Yes); + } + } + (certainty, NestedNormalizationGoals(goals)) + } else { + let certainty = certainty.unify_with(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + }; + let external_constraints = + self.compute_external_query_constraints(normalization_nested_goals)?; let (var_values, mut external_constraints) = - (var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); + (self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); // Remove any trivial region constraints once we've resolved regions external_constraints .region_constraints @@ -146,6 +167,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints( &self, + normalization_nested_goals: NestedNormalizationGoals<'tcx>, ) -> Result, NoSolution> { // We only check for leaks from universes which were entered inside // of the query. @@ -176,7 +198,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) }); - Ok(ExternalConstraintsData { region_constraints, opaque_types }) + Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }) } /// After calling a canonical query, we apply the constraints returned @@ -185,13 +207,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// This happens in three steps: /// - we instantiate the bound variables of the query response /// - we unify the `var_values` of the response with the `original_values` - /// - we apply the `external_constraints` returned by the query + /// - we apply the `external_constraints` returned by the query, returning + /// the `normalization_nested_goals` pub(super) fn instantiate_and_apply_query_response( &mut self, param_env: ty::ParamEnv<'tcx>, original_values: Vec>, response: CanonicalResponse<'tcx>, - ) -> Certainty { + ) -> (NestedNormalizationGoals<'tcx>, Certainty) { let instantiation = Self::compute_query_response_instantiation_values( self.infcx, &original_values, @@ -203,11 +226,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values); - let ExternalConstraintsData { region_constraints, opaque_types } = - external_constraints.deref(); + let ExternalConstraintsData { + region_constraints, + opaque_types, + normalization_nested_goals, + } = external_constraints.deref(); self.register_region_constraints(region_constraints); self.register_new_opaque_types(param_env, opaque_types); - certainty + (normalization_nested_goals.clone(), certainty) } /// This returns the canoncial variable values to instantiate the bound variables of diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs index 67b6801059a..c8f9a461adf 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs @@ -11,6 +11,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { infcx: self.infcx, variables: self.variables, var_values: self.var_values, + is_normalizes_to_goal: self.is_normalizes_to_goal, predefined_opaques_in_body: self.predefined_opaques_in_body, max_input_universe: self.max_input_universe, search_graph: self.search_graph, @@ -25,6 +26,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { infcx: _, variables: _, var_values: _, + is_normalizes_to_goal: _, predefined_opaques_in_body: _, max_input_universe: _, search_graph: _, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 3b858cb449f..a0fe6eca0fc 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -7,14 +7,14 @@ use rustc_infer::infer::{ BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::MaybeCause; +use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals}; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ - CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, + CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaques, PredefinedOpaquesData, + QueryResult, }; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::{ @@ -61,6 +61,14 @@ pub struct EvalCtxt<'a, 'tcx> { /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. variables: CanonicalVarInfos<'tcx>, + /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals, + /// `NormalizesTo` goals act like functions with the expected term always being + /// fully unconstrained. This would weaken inference however, as the nested goals + /// never get the inference constraints from the actual normalized-to type. Because + /// of this we return any ambiguous nested goals from `NormalizesTo` to the caller + /// when then adds these to its own context. The caller is always an `AliasRelate` + /// goal so this never leaks out of the solver. + is_normalizes_to_goal: bool, pub(super) var_values: CanonicalVarValues<'tcx>, predefined_opaques_in_body: PredefinedOpaques<'tcx>, @@ -91,9 +99,9 @@ pub struct EvalCtxt<'a, 'tcx> { pub(super) inspect: ProofTreeBuilder<'tcx>, } -#[derive(Debug, Clone)] +#[derive(Default, Debug, Clone)] pub(super) struct NestedGoals<'tcx> { - /// This normalizes-to goal that is treated specially during the evaluation + /// These normalizes-to goals are treated specially during the evaluation /// loop. In each iteration we take the RHS of the projection, replace it with /// a fresh inference variable, and only after evaluating that goal do we /// equate the fresh inference variable with the actual RHS of the predicate. @@ -101,26 +109,24 @@ pub(super) struct NestedGoals<'tcx> { /// This is both to improve caching, and to avoid using the RHS of the /// projection predicate to influence the normalizes-to candidate we select. /// - /// This is not a 'real' nested goal. We must not forget to replace the RHS - /// with a fresh inference variable when we evaluate this goal. That can result - /// in a trait solver cycle. This would currently result in overflow but can be - /// can be unsound with more powerful coinduction in the future. - pub(super) normalizes_to_hack_goal: Option>>, + /// Forgetting to replace the RHS with a fresh inference variable when we evaluate + /// this goal results in an ICE.. + pub(super) normalizes_to_goals: Vec>>, /// The rest of the goals which have not yet processed or remain ambiguous. pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>, } impl<'tcx> NestedGoals<'tcx> { pub(super) fn new() -> Self { - Self { normalizes_to_hack_goal: None, goals: Vec::new() } + Self { normalizes_to_goals: Vec::new(), goals: Vec::new() } } pub(super) fn is_empty(&self) -> bool { - self.normalizes_to_hack_goal.is_none() && self.goals.is_empty() + self.normalizes_to_goals.is_empty() && self.goals.is_empty() } pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) { - assert_eq!(other.normalizes_to_hack_goal, None); + self.normalizes_to_goals.extend(other.normalizes_to_goals); self.goals.extend(other.goals) } } @@ -155,6 +161,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.search_graph.solver_mode() } + pub(super) fn set_is_normalizes_to_goal(&mut self) { + self.is_normalizes_to_goal = true; + } + /// Creates a root evaluation context and search graph. This should only be /// used from outside of any evaluation, and other methods should be preferred /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). @@ -167,8 +177,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut search_graph = search_graph::SearchGraph::new(mode); let mut ecx = EvalCtxt { - search_graph: &mut search_graph, infcx, + search_graph: &mut search_graph, nested_goals: NestedGoals::new(), inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), @@ -180,6 +190,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { max_input_universe: ty::UniverseIndex::ROOT, variables: ty::List::empty(), var_values: CanonicalVarValues::dummy(), + is_normalizes_to_goal: false, tainted: Ok(()), }; let result = f(&mut ecx); @@ -233,6 +244,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { infcx, variables: canonical_input.variables, var_values, + is_normalizes_to_goal: false, predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.max_universe, search_graph, @@ -319,6 +331,27 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>, ) -> Result<(bool, Certainty), NoSolution> { + let (normalization_nested_goals, has_changed, certainty) = + self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?; + assert!(normalization_nested_goals.is_empty()); + Ok((has_changed, certainty)) + } + + /// Recursively evaluates `goal`, returning the nested goals in case + /// the nested goal is a `NormalizesTo` goal. + /// + /// As all other goal kinds do not return any nested goals and + /// `NormalizesTo` is only used by `AliasRelate`, all other callsites + /// should use [`EvalCtxt::evaluate_goal`] which discards that empty + /// storage. + // FIXME(-Znext-solver=coinduction): `_source` is currently unused but will + // be necessary once we implement the new coinduction approach. + fn evaluate_goal_raw( + &mut self, + goal_evaluation_kind: GoalEvaluationKind, + _source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(NestedNormalizationGoals<'tcx>, bool, Certainty), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); @@ -336,12 +369,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { Ok(response) => response, }; - let (certainty, has_changed) = self.instantiate_response_discarding_overflow( - goal.param_env, - source, - orig_values, - canonical_response, - ); + let (normalization_nested_goals, certainty, has_changed) = self + .instantiate_response_discarding_overflow( + goal.param_env, + orig_values, + canonical_response, + ); self.inspect.goal_evaluation(goal_evaluation); // FIXME: We previously had an assert here that checked that recomputing // a goal after applying its constraints did not change its response. @@ -353,47 +386,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // Once we have decided on how to handle trait-system-refactor-initiative#75, // we should re-add an assert here. - Ok((has_changed, certainty)) + Ok((normalization_nested_goals, has_changed, certainty)) } fn instantiate_response_discarding_overflow( &mut self, param_env: ty::ParamEnv<'tcx>, - source: GoalSource, original_values: Vec>, response: CanonicalResponse<'tcx>, - ) -> (Certainty, bool) { - // The old solver did not evaluate nested goals when normalizing. - // It returned the selection constraints allowing a `Projection` - // obligation to not hold in coherence while avoiding the fatal error - // from overflow. - // - // We match this behavior here by considering all constraints - // from nested goals which are not from where-bounds. We will already - // need to track which nested goals are required by impl where-bounds - // for coinductive cycles, so we simply reuse that here. - // - // While we could consider overflow constraints in more cases, this should - // not be necessary for backcompat and results in better perf. It also - // avoids a potential inconsistency which would otherwise require some - // tracking for root goals as well. See #119071 for an example. - let keep_overflow_constraints = || { - self.search_graph.current_goal_is_normalizes_to() - && source != GoalSource::ImplWhereBound - }; - - if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty - && !keep_overflow_constraints() - { - return (response.value.certainty, false); + ) -> (NestedNormalizationGoals<'tcx>, Certainty, bool) { + if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty { + return (NestedNormalizationGoals::empty(), response.value.certainty, false); } let has_changed = !response.value.var_values.is_identity_modulo_regions() || !response.value.external_constraints.opaque_types.is_empty(); - let certainty = + let (normalization_nested_goals, certainty) = self.instantiate_and_apply_query_response(param_env, original_values, response); - (certainty, has_changed) + (normalization_nested_goals, certainty, has_changed) } fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { @@ -496,7 +507,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { let tcx = self.tcx(); - let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); + let mut goals = core::mem::take(&mut self.nested_goals); self.inspect.evaluate_added_goals_loop_start(); @@ -508,7 +519,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - if let Some(goal) = goals.normalizes_to_hack_goal.take() { + for goal in goals.normalizes_to_goals { // Replace the goal with an unconstrained infer var, so the // RHS does not affect projection candidate assembly. let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); @@ -517,11 +528,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, ); - let (_, certainty) = self.evaluate_goal( - GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }, + let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( + GoalEvaluationKind::Nested, GoalSource::Misc, unconstrained_goal, )?; + // Add the nested goals from normalization to our own nested goals. + goals.goals.extend(nested_goals); // Finally, equate the goal's RHS with the unconstrained var. // We put the nested goals from this into goals instead of @@ -536,27 +549,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // looking at the "has changed" return from evaluate_goal, // because we expect the `unconstrained_rhs` part of the predicate // to have changed -- that means we actually normalized successfully! - if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) { + let with_resolved_vars = self.resolve_vars_if_possible(goal); + if goal.predicate.alias != with_resolved_vars.predicate.alias { unchanged_certainty = None; } match certainty { Certainty::Yes => {} Certainty::Maybe(_) => { - // We need to resolve vars here so that we correctly - // deal with `has_changed` in the next iteration. - self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal)); + self.nested_goals.normalizes_to_goals.push(with_resolved_vars); unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); } } } - for (source, goal) in goals.goals.drain(..) { - let (has_changed, certainty) = self.evaluate_goal( - GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No }, - source, - goal, - )?; + for (source, goal) in goals.goals { + let (has_changed, certainty) = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; if has_changed { unchanged_certainty = None; } @@ -968,7 +977,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty: Ty<'tcx>, ) -> Option> { use rustc_middle::mir::interpret::ErrorHandled; - match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) { + match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, DUMMY_SP) { Ok(ct) => Some(ct), Err(ErrorHandled::Reported(e, _)) => { Some(ty::Const::new_error(self.tcx(), e.into(), ty)) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 91fd48807a4..5b1124e8b9f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -24,6 +24,7 @@ where infcx: outer_ecx.infcx, variables: outer_ecx.variables, var_values: outer_ecx.var_values, + is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal, predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, max_input_universe: outer_ecx.max_input_universe, search_graph: outer_ecx.search_graph, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 3262d64cb7d..e0c7804b6db 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -58,12 +58,13 @@ impl<'tcx> InferCtxt<'tcx> { } let candidate = candidates.pop().unwrap(); - let certainty = ecx.instantiate_and_apply_query_response( - trait_goal.param_env, - orig_values, - candidate.result, - ); - + let (normalization_nested_goals, certainty) = ecx + .instantiate_and_apply_query_response( + trait_goal.param_env, + orig_values, + candidate.result, + ); + assert!(normalization_nested_goals.is_empty()); Ok(Some((candidate, certainty))) }); diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 9e3e6a4676e..cfec2e9bbf3 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -70,7 +70,19 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { instantiated_goals.push(goal); } - for &goal in &instantiated_goals { + for goal in instantiated_goals.iter().copied() { + // We need to be careful with `NormalizesTo` goals as the + // expected term has to be replaced with an unconstrained + // inference variable. + if let Some(kind) = goal.predicate.kind().no_bound_vars() + && let ty::PredicateKind::NormalizesTo(predicate) = kind + && !predicate.alias.is_opaque(infcx.tcx) + { + // FIXME: We currently skip these goals as + // `fn evaluate_root_goal` ICEs if there are any + // `NestedNormalizationGoals`. + continue; + }; let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes); let proof_tree = proof_tree.unwrap(); try_visit!(visitor.visit_goal(&InspectGoal::new( diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index f7b310a7abe..4da999f2406 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -7,7 +7,7 @@ use std::mem; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ - CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult, + CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult, }; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::DumpSolverProofTree; @@ -97,9 +97,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> { WipGoalEvaluationKind::Root { orig_values } => { inspect::GoalEvaluationKind::Root { orig_values } } - WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => { - inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack } - } + WipGoalEvaluationKind::Nested => inspect::GoalEvaluationKind::Nested, }, evaluation: self.evaluation.unwrap().finalize(), } @@ -109,7 +107,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> { #[derive(Eq, PartialEq, Debug)] pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> { Root { orig_values: Vec> }, - Nested { is_normalizes_to_hack: IsNormalizesToHack }, + Nested, } #[derive(Eq, PartialEq)] @@ -305,9 +303,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { solve::GoalEvaluationKind::Root => { WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() } } - solve::GoalEvaluationKind::Nested { is_normalizes_to_hack } => { - WipGoalEvaluationKind::Nested { is_normalizes_to_hack } - } + solve::GoalEvaluationKind::Nested => WipGoalEvaluationKind::Nested, }, evaluation: None, }) @@ -419,6 +415,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } + pub fn add_normalizes_to_goal( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) { + if ecx.inspect.is_noop() { + return; + } + + Self::add_goal(ecx, GoalSource::Misc, goal.with(ecx.tcx(), goal.predicate)); + } + pub fn add_goal( ecx: &mut EvalCtxt<'_, 'tcx>, source: GoalSource, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 0bf28f520a4..8294a8a67b1 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -18,8 +18,7 @@ use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ - CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, - QueryResult, Response, + CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response, }; use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ @@ -69,7 +68,7 @@ enum SolverMode { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum GoalEvaluationKind { Root, - Nested { is_normalizes_to_hack: IsNormalizesToHack }, + Nested, } #[extension(trait CanonicalResponseExt)] @@ -202,12 +201,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] - fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { - assert!( - self.nested_goals.normalizes_to_hack_goal.is_none(), - "attempted to set the projection eq hack goal when one already exists" - ); - self.nested_goals.normalizes_to_hack_goal = Some(goal); + fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { + inspect::ProofTreeBuilder::add_normalizes_to_goal(self, goal); + self.nested_goals.normalizes_to_goals.push(goal); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs index 911462f4b9a..37d56452893 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs @@ -16,7 +16,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .no_bound_vars() .expect("const ty should not rely on other generics"), ) { - self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.instantiate_normalizes_to_term(goal, normalized_const.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs index 52d2fe1e3ec..d60490bce44 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs @@ -16,7 +16,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { let tcx = self.tcx(); let inherent = goal.predicate.alias; - let expected = goal.predicate.term.ty().expect("inherent consts are treated separately"); let impl_def_id = tcx.parent(inherent.def_id); let impl_args = self.fresh_args_for_item(impl_def_id); @@ -30,12 +29,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Equate IAT with the RHS of the project goal let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx); - self.eq( - goal.param_env, - expected, - tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args), - ) - .expect("expected goal term to be fully unconstrained"); // Check both where clauses on the impl and IAT // @@ -51,6 +44,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .map(|(pred, _)| goal.with(tcx, pred)), ); + let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args); + self.instantiate_normalizes_to_term(goal, normalized.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index a45c1c34410..d24bec5a766 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -31,32 +31,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let def_id = goal.predicate.def_id(); + let def_kind = self.tcx().def_kind(def_id); + match def_kind { + DefKind::OpaqueTy => return self.normalize_opaque_type(goal), + _ => self.set_is_normalizes_to_goal(), + } + + debug_assert!(self.term_is_fully_unconstrained(goal)); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { match self.tcx().associated_item(def_id).container { ty::AssocItemContainer::TraitContainer => { - // To only compute normalization once for each projection we only - // assemble normalization candidates if the expected term is an - // unconstrained inference variable. - // - // Why: For better cache hits, since if we have an unconstrained RHS then - // there are only as many cache keys as there are (canonicalized) alias - // types in each normalizes-to goal. This also weakens inference in a - // forwards-compatible way so we don't use the value of the RHS term to - // affect candidate assembly for projections. - // - // E.g. for `::Assoc == u32` we recursively compute the goal - // `exists ::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver, since we're piggybacking off of regular - // goal caching. - if self.term_is_fully_unconstrained(goal) { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) } ty::AssocItemContainer::ImplContainer => { self.normalize_inherent_associated_type(goal) @@ -64,9 +51,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } DefKind::AnonConst => self.normalize_anon_const(goal), - DefKind::OpaqueTy => self.normalize_opaque_type(goal), DefKind::TyAlias => self.normalize_weak_type(goal), - kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)), } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs index 9f91c02c1ab..13af5068b6c 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs @@ -15,10 +15,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { let tcx = self.tcx(); let weak_ty = goal.predicate.alias; - let expected = goal.predicate.term.ty().expect("no such thing as a const alias"); - - let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); - self.eq(goal.param_env, expected, actual)?; // Check where clauses self.add_goals( @@ -30,6 +26,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .map(|pred| goal.with(tcx, pred)), ); + let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); + self.instantiate_normalizes_to_term(goal, actual.into()); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 07a8aca85a0..a48b2f2478b 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -10,7 +10,6 @@ use rustc_index::IndexVec; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::traits::solve::CacheData; use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; -use rustc_middle::ty; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use std::mem; @@ -175,15 +174,6 @@ impl<'tcx> SearchGraph<'tcx> { } } - pub(super) fn current_goal_is_normalizes_to(&self) -> bool { - self.stack.raw.last().map_or(false, |e| { - matches!( - e.input.value.goal.predicate.kind().skip_binder(), - ty::PredicateKind::NormalizesTo(..) - ) - }) - } - /// Returns the remaining depth allowed for nested goals. /// /// This is generally simply one less than the current depth. diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index fbf96833187..0796ffcbc45 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -786,7 +786,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { match selcx.infcx.const_eval_resolve( obligation.param_env, unevaluated, - Some(obligation.cause.span), + obligation.cause.span, ) { Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())), Ok(None) => { diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 7f0f9a12d6a..9ca1dd4557d 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -68,7 +68,7 @@ pub fn is_const_evaluatable<'tcx>( tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } ty::ConstKind::Unevaluated(uv) => { - let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); + let concrete = infcx.const_eval_resolve(param_env, uv, span); match concrete { Err(ErrorHandled::TooGeneric(_)) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( @@ -99,7 +99,7 @@ pub fn is_const_evaluatable<'tcx>( // and hopefully soon change this to an error. // // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); + let concrete = infcx.const_eval_resolve(param_env, uv, span); match concrete { // If we're evaluating a generic foreign constant, under a nightly compiler while // the current crate does not enable `feature(generic_const_exprs)`, abort diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 067ca883bd8..ee390d9c9f0 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -194,8 +194,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string()))); // Suggest `fn foo(t: T) where ::A: Bound`. - // FIXME: once `#![feature(associated_type_bounds)]` is stabilized, we should suggest - // `fn foo(t: impl Trait)` instead. + // FIXME: we should suggest `fn foo(t: impl Trait)` instead. err.multipart_suggestion( "introduce a type parameter with a trait bound instead of using `impl Trait`", sugg, @@ -3510,9 +3509,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); - if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); - } + tcx.disabled_nightly_features( + err, + Some(tcx.local_def_id_to_hir_id(body_id)), + [(String::new(), sym::trivial_bounds)], + ); } ObligationCauseCode::OpaqueReturnType(expr_info) => { if let Some((expr_ty, expr_span)) = expr_info { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 2fd64f474d5..34c891d400e 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -600,7 +600,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { obligation.param_env, unevaluated, c.ty(), - Some(obligation.cause.span), + obligation.cause.span, ) { Ok(val) => Ok(val), Err(e) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d10fe6a9490..be71eac6948 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -949,7 +949,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, unevaluated, c.ty(), - Some(obligation.cause.span), + obligation.cause.span, ) { Ok(val) => Ok(val), Err(e) => Err(e), diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index e871c4659b4..b6f937ebe47 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -89,6 +89,7 @@ mod rustc { use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::ValTree; + use rustc_span::DUMMY_SP; /// The source and destination types of a transmutation. #[derive(TypeVisitable, Debug, Clone, Copy)] @@ -135,7 +136,7 @@ mod rustc { use rustc_middle::ty::ScalarInt; use rustc_span::symbol::sym; - let Ok(cv) = c.eval(tcx, param_env, None) else { + let Ok(cv) = c.eval(tcx, param_env, DUMMY_SP) else { return Some(Self { alignment: true, lifetimes: true, diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index a0759f7df7f..112f617fe16 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -148,19 +148,20 @@ pub enum PredicateKind { /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. Ambiguous, - /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the alias - /// cannot be normalized in the current context. + /// This should only be used inside of the new solver for `AliasRelate` and expects + /// the `term` to be an unconstrained inference variable. /// - /// `Projection(::Assoc, ?x)` results in `?x == ::Assoc` while - /// `NormalizesTo(::Assoc, ?x)` results in `NoSolution`. - /// - /// Only used in the new solver. + /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the + /// alias cannot be normalized in the current context. For the rigid alias + /// `T as Trait>::Assoc`, `Projection(::Assoc, ?x)` constrains `?x` + /// to `::Assoc` while `NormalizesTo(::Assoc, ?x)` + /// results in `NoSolution`. NormalizesTo(I::NormalizesTo), /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. /// This predicate requires two terms to be equal to eachother. /// - /// Only used for new solver + /// Only used for new solver. AliasRelate(I::Term, I::Term, AliasRelationDirection), } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 304f607000b..f47fe560b07 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -155,7 +155,6 @@ use core::error::Error; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; -use core::intrinsics::retag_box_to_raw; use core::iter::FusedIterator; use core::marker::Tuple; use core::marker::Unsize; @@ -165,7 +164,7 @@ use core::ops::{ CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DispatchFromDyn, Receiver, }; use core::pin::Pin; -use core::ptr::{self, NonNull, Unique}; +use core::ptr::{self, addr_of_mut, NonNull, Unique}; use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] @@ -1111,16 +1110,12 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { - // This is the transition point from `Box` to raw pointers. For Stacked Borrows, these casts - // are relevant -- if this is a global allocator Box and we just get the pointer from `b.0`, - // it will have `Unique` permission, which is not what we want from a raw pointer. We could - // fix that by going through `&mut`, but then if this is *not* a global allocator Box, we'd - // be adding uniqueness assertions that we do not want. So for Miri's sake we pass this - // pointer through an intrinsic for box-to-raw casts, which can do the right thing wrt the - // aliasing model. - let b = mem::ManuallyDrop::new(b); + let mut b = mem::ManuallyDrop::new(b); + // We carefully get the raw pointer out in a way that Miri's aliasing model understands what + // is happening: using the primitive "deref" of `Box`. + let ptr = addr_of_mut!(**b); let alloc = unsafe { ptr::read(&b.1) }; - (unsafe { retag_box_to_raw::(b.0.as_ptr()) }, alloc) + (ptr, alloc) } #[unstable( diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index ca504b05a96..02d155aaf12 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -173,12 +173,12 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] -#![feature(associated_type_bounds)] #![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(const_mut_refs)] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index f2f42e63d6b..db56b8d07c7 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1462,7 +1462,7 @@ impl Vec { /// /// The removed element is replaced by the last element of the vector. /// - /// This does not preserve ordering, but is *O*(1). + /// This does not preserve ordering of the remaining elements, but is *O*(1). /// If you need to preserve the element order, use [`remove`] instead. /// /// [`remove`]: Vec::remove diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index e8496989bcf..f58998fbd79 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(allocator_api)] #![feature(alloc_layout_extra)] #![feature(iter_array_chunks)] @@ -22,7 +23,6 @@ #![feature(try_reserve_kind)] #![feature(try_with_capacity)] #![feature(unboxed_closures)] -#![feature(associated_type_bounds)] #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] diff --git a/library/core/src/default.rs b/library/core/src/default.rs index a1303fcd821..a5075554682 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -71,6 +71,8 @@ use crate::ascii::Char as AsciiChar; /// /// You cannot use the `#[default]` attribute on non-unit or non-exhaustive variants. /// +/// The `#[default]` attribute was stabilized in Rust 1.62.0. +/// /// ## How can I implement `Default`? /// /// Provide an implementation for the `default()` method that returns the value of diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 86b9a39d68a..f939720287f 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2709,19 +2709,6 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize { unreachable!() } -/// Retag a box pointer as part of casting it to a raw pointer. This is the `Box` equivalent of -/// `(x: &mut T) as *mut T`. The input pointer must be the pointer of a `Box` (passed as raw pointer -/// to avoid all questions around move semantics and custom allocators), and `A` must be the `Box`'s -/// allocator. -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_nounwind] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(bootstrap, inline)] -pub unsafe fn retag_box_to_raw(ptr: *mut T) -> *mut T { - // Miri needs to adjust the provenance, but for regular codegen this is not needed - ptr -} - // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9b786feba89..f317584371e 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -112,6 +112,7 @@ // // Library features: // tidy-alphabetical-start +#![feature(array_ptr_get)] #![feature(char_indices_offset)] #![feature(const_align_of_val)] #![feature(const_align_of_val_raw)] @@ -202,6 +203,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(associated_type_bounds))] #![cfg_attr(bootstrap, feature(diagnostic_namespace))] #![cfg_attr(bootstrap, feature(exhaustive_patterns))] #![cfg_attr(bootstrap, feature(platform_intrinsics))] @@ -212,7 +214,6 @@ #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_const)] -#![feature(associated_type_bounds)] #![feature(auto_traits)] #![feature(c_unwind)] #![feature(cfg_sanitize)] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 1f7a4e276f5..a8f637280df 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -5,7 +5,7 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::intrinsics; use crate::marker::{Freeze, StructuralPartialEq}; -use crate::ops::{BitOr, BitOrAssign, Div, Neg, Rem}; +use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::ptr; use crate::str::FromStr; @@ -849,6 +849,16 @@ macro_rules! nonzero_integer_signedness_dependent_impls { } } + #[stable(feature = "nonzero_div_assign", since = "CURRENT_RUSTC_VERSION")] + impl DivAssign<$Ty> for $Int { + /// This operation rounds towards zero, + /// truncating any fractional part of the exact result, and cannot panic. + #[inline] + fn div_assign(&mut self, other: $Ty) { + *self = *self / other; + } + } + #[stable(feature = "nonzero_div", since = "1.51.0")] impl Rem<$Ty> for $Int { type Output = $Int; @@ -861,6 +871,15 @@ macro_rules! nonzero_integer_signedness_dependent_impls { unsafe { intrinsics::unchecked_rem(self, other.get()) } } } + + #[stable(feature = "nonzero_div_assign", since = "CURRENT_RUSTC_VERSION")] + impl RemAssign<$Ty> for $Int { + /// This operation satisfies `n % d == n - (n / d) * d`, and cannot panic. + #[inline] + fn rem_assign(&mut self, other: $Ty) { + *self = *self % other; + } + } }; // Impls for signed nonzero types only. diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a0c04d3f65d..979fd1e4b4a 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1798,6 +1798,46 @@ impl *const [T] { } } +impl *const [T; N] { + /// Returns a raw pointer to the array's buffer. + /// + /// This is equivalent to casting `self` to `*const T`, but more type-safe. + /// + /// # Examples + /// + /// ```rust + /// #![feature(array_ptr_get)] + /// use std::ptr; + /// + /// let arr: *const [i8; 3] = ptr::null(); + /// assert_eq!(arr.as_ptr(), ptr::null()); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_ptr(self) -> *const T { + self as *const T + } + + /// Returns a raw pointer to a slice containing the entire array. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_ptr_get, slice_ptr_len)] + /// + /// let arr: *const [i32; 3] = &[1, 2, 4] as *const [i32; 3]; + /// let slice: *const [i32] = arr.as_slice(); + /// assert_eq!(slice.len(), 3); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_slice(self) -> *const [T] { + self + } +} + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 6a9033a144d..bfdd9dba320 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -2209,6 +2209,49 @@ impl *mut [T] { } } +impl *mut [T; N] { + /// Returns a raw pointer to the array's buffer. + /// + /// This is equivalent to casting `self` to `*mut T`, but more type-safe. + /// + /// # Examples + /// + /// ```rust + /// #![feature(array_ptr_get)] + /// use std::ptr; + /// + /// let arr: *mut [i8; 3] = ptr::null_mut(); + /// assert_eq!(arr.as_mut_ptr(), ptr::null_mut()); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_mut_ptr(self) -> *mut T { + self as *mut T + } + + /// Returns a raw pointer to a mutable slice containing the entire array. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_ptr_get)] + /// + /// let mut arr = [1, 2, 5]; + /// let ptr: *mut [i32; 3] = &mut arr; + /// unsafe { + /// (&mut *ptr.as_mut_slice())[..2].copy_from_slice(&[3, 4]); + /// } + /// assert_eq!(arr, [3, 4, 5]); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_mut_slice(self) -> *mut [T] { + self + } +} + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index c5a7e87c4aa..421062f5873 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,5 +1,6 @@ #![feature(alloc_layout_extra)] #![feature(array_chunks)] +#![feature(array_ptr_get)] #![feature(array_windows)] #![feature(ascii_char)] #![feature(ascii_char_variants)] @@ -52,6 +53,7 @@ #![feature(sort_internals)] #![feature(slice_take)] #![feature(slice_from_ptr_range)] +#![feature(slice_ptr_len)] #![feature(slice_split_once)] #![feature(split_as_slice)] #![feature(maybe_uninit_fill)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 659fbd255c1..5c518e2d593 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1141,3 +1141,16 @@ fn test_const_copy() { assert!(*ptr2 == 1); }; } + +#[test] +fn test_null_array_as_slice() { + let arr: *mut [u8; 4] = null_mut(); + let ptr: *mut [u8] = arr.as_mut_slice(); + assert!(ptr.is_null()); + assert_eq!(ptr.len(), 4); + + let arr: *const [u8; 4] = null(); + let ptr: *const [u8] = arr.as_slice(); + assert!(ptr.is_null()); + assert_eq!(ptr.len(), 4); +} diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 98495f25bc6..340a5c87f0b 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -15,7 +15,8 @@ use std::{ }; use bootstrap::{ - find_recent_config_change_ids, t, Build, Config, Subcommand, CONFIG_CHANGE_HISTORY, + find_recent_config_change_ids, human_readable_changes, t, Build, Config, Subcommand, + CONFIG_CHANGE_HISTORY, }; fn main() { @@ -164,14 +165,7 @@ fn check_version(config: &Config) -> Option { } msg.push_str("There have been changes to x.py since you last updated:\n"); - - for change in changes { - msg.push_str(&format!(" [{}] {}\n", change.severity, change.summary)); - msg.push_str(&format!( - " - PR Link https://github.com/rust-lang/rust/pull/{}\n", - change.change_id - )); - } + msg.push_str(&human_readable_changes(&changes)); msg.push_str("NOTE: to silence this warning, "); msg.push_str(&format!( diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index e927b491c71..2076444ca0c 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -550,6 +550,10 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car cargo.rustflag("-Cforce-unwind-tables=yes"); } + // Enable frame pointers by default for the library. Note that they are still controlled by a + // separate setting for the compiler. + cargo.rustflag("-Cforce-frame-pointers=yes"); + let html_root = format!("-Zcrate-attr=doc(html_root_url=\"{}/\")", builder.doc_rust_lang_org_channel(),); cargo.rustflag(&html_root); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 701bd585eee..3da927b5fa0 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -564,11 +564,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = output(cmd.arg("--version")); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 16 { + if major >= 17 { return; } } - panic!("\n\nbad LLVM version: {version}, need >=16.0\n\n") + panic!("\n\nbad LLVM version: {version}, need >=17.0\n\n") } fn configure_cmake( diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 690e20fb6b9..6d3163b90b1 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1548,6 +1548,10 @@ impl Step for MirOpt { }) }; + run(self.target); + + // Run more targets with `--bless`. But we always run the host target first, since some + // tests use very specific `only` clauses that are not covered by the target set below. if builder.config.cmd.bless() { // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort, // but while we're at it we might as well flex our cross-compilation support. This @@ -1566,8 +1570,6 @@ impl Step for MirOpt { }); run(panic_abort_target); } - } else { - run(self.target); } } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 3c200112103..deab3fce54c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -692,6 +692,79 @@ impl Step for RustAnalyzerProcMacroSrv { } } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct LlvmBitcodeLinker { + pub compiler: Compiler, + pub target: TargetSelection, + pub extra_features: Vec, +} + +impl Step for LlvmBitcodeLinker { + type Output = PathBuf; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let builder = run.builder; + run.path("src/tools/llvm-bitcode-linker").default_condition( + builder.config.extended + && builder + .config + .tools + .as_ref() + .map_or(builder.build.unstable_features(), |tools| { + tools.iter().any(|tool| tool == "llvm-bitcode-linker") + }), + ) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(LlvmBitcodeLinker { + compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + extra_features: Vec::new(), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + let bin_name = "llvm-bitcode-linker"; + + builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); + builder.ensure(compile::Rustc::new(self.compiler, self.target)); + + let mut cargo = prepare_tool_cargo( + builder, + self.compiler, + Mode::ToolRustc, + self.target, + "build", + "src/tools/llvm-bitcode-linker", + SourceType::InTree, + &self.extra_features, + ); + + if builder.config.rustc_parallel { + cargo.rustflag("--cfg=parallel_compiler"); + } + + builder.run(&mut cargo.into()); + + let tool_out = builder + .cargo_out(self.compiler, Mode::ToolRustc, self.target) + .join(exe(bin_name, self.compiler.host)); + + if self.compiler.stage > 0 { + let bindir = builder.sysroot(self.compiler).join("bin"); + t!(fs::create_dir_all(&bindir)); + let bin_destination = bindir.join(exe(bin_name, self.compiler.host)); + builder.copy_link(&tool_out, &bin_destination); + bin_destination + } else { + tool_out + } + } +} + macro_rules! tool_extended { (($sel:ident, $builder:ident), $($name:ident, @@ -795,7 +868,6 @@ tool_extended!((self, builder), Rls, "src/tools/rls", "rls", stable=true, tool_std=true; RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true; Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"]; - LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", stable=false, add_bins_to_sysroot = ["llvm-bitcode-linker"]; ); impl<'a> Builder<'a> { diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 3e1bc9a9acd..67cde01ccdb 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -606,7 +606,8 @@ impl Target { #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub(crate) struct TomlConfig { changelog_seen: Option, // FIXME: Deprecated field. Remove it at 2024. - change_id: Option, + #[serde(flatten)] + change_id: ChangeIdWrapper, build: Option, install: Option, llvm: Option, @@ -616,6 +617,16 @@ pub(crate) struct TomlConfig { profile: Option, } +/// Since we use `#[serde(deny_unknown_fields)]` on `TomlConfig`, we need a wrapper type +/// for the "change-id" field to parse it even if other fields are invalid. This ensures +/// that if deserialization fails due to other fields, we can still provide the changelogs +/// to allow developers to potentially find the reason for the failure in the logs.. +#[derive(Deserialize, Default)] +pub(crate) struct ChangeIdWrapper { + #[serde(alias = "change-id")] + pub(crate) inner: Option, +} + /// Describes how to handle conflicts in merging two [`TomlConfig`] #[derive(Copy, Clone, Debug)] enum ReplaceOpt { @@ -657,7 +668,7 @@ impl Merge for TomlConfig { } } self.changelog_seen.merge(changelog_seen, replace); - self.change_id.merge(change_id, replace); + self.change_id.inner.merge(change_id.inner, replace); do_merge(&mut self.build, build, replace); do_merge(&mut self.install, install, replace); do_merge(&mut self.llvm, llvm, replace); @@ -1210,6 +1221,20 @@ impl Config { toml::from_str(&contents) .and_then(|table: toml::Value| TomlConfig::deserialize(table)) .unwrap_or_else(|err| { + if let Ok(Some(changes)) = toml::from_str(&contents) + .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)) + .and_then(|change_id| { + Ok(change_id.inner.map(|id| crate::find_recent_config_change_ids(id))) + }) + { + if !changes.is_empty() { + println!( + "WARNING: There have been changes to x.py since you last updated:\n{}", + crate::human_readable_changes(&changes) + ); + } + } + eprintln!("failed to parse TOML configuration '{}': {err}", file.display()); exit!(2); }) @@ -1376,7 +1401,7 @@ impl Config { toml.merge(override_toml, ReplaceOpt::Override); config.changelog_seen = toml.changelog_seen; - config.change_id = toml.change_id; + config.change_id = toml.change_id.inner; let Build { build, diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 6ac573c68df..93ba5f4120a 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -1,4 +1,4 @@ -use super::{flags::Flags, Config}; +use super::{flags::Flags, ChangeIdWrapper, Config}; use crate::core::config::{LldMode, TomlConfig}; use clap::CommandFactory; @@ -237,3 +237,20 @@ fn rust_lld() { assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External)); assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused)); } + +#[test] +#[should_panic] +fn parse_config_with_unknown_field() { + parse("unknown-key = 1"); +} + +#[test] +fn parse_change_id_with_unknown_field() { + let config = r#" + change-id = 3461 + unknown-key = 1 + "#; + + let change_id_wrapper: ChangeIdWrapper = toml::from_str(config).unwrap(); + assert_eq!(change_id_wrapper.inner, Some(3461)); +} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index f0fe994d155..64bb7bf01f7 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -50,7 +50,9 @@ mod utils; pub use core::builder::PathSet; pub use core::config::flags::Subcommand; pub use core::config::Config; -pub use utils::change_tracker::{find_recent_config_change_ids, CONFIG_CHANGE_HISTORY}; +pub use utils::change_tracker::{ + find_recent_config_change_ids, human_readable_changes, CONFIG_CHANGE_HISTORY, +}; const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 14c1dc07306..277ec00fa62 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -60,6 +60,20 @@ pub fn find_recent_config_change_ids(current_id: usize) -> Vec { .collect() } +pub fn human_readable_changes(changes: &[ChangeInfo]) -> String { + let mut message = String::new(); + + for change in changes { + message.push_str(&format!(" [{}] {}\n", change.severity, change.summary)); + message.push_str(&format!( + " - PR Link https://github.com/rust-lang/rust/pull/{}\n", + change.change_id + )); + } + + message +} + /// Keeps track of major changes made to the bootstrap configuration. /// /// If you make any major changes (such as adding new values or changing default values), diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile deleted file mode 100644 index 4fc2b2e507e..00000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -FROM ubuntu:23.04 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-16-tools \ - llvm-16-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Note: libgccjit needs to match the default gcc version for the linker to find it. - -# Install powershell (universal package) so we can test x.ps1 on Linux -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 - -# This is not the latest LLVM version, so some components required by tests may -# be missing. -ENV IS_NOT_LATEST_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-16 \ - --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 - -COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ - -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ - -RUN /scripts/build-gccjit.sh /scripts - -ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index 7c2ecd198e2..538962802c9 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -56,11 +56,10 @@ ENV RUST_CONFIGURE_ARGS \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ - COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts +COPY scripts/x86_64-gnu-llvm.sh /tmp/script.sh ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index e8383500dfc..3476b10a3ad 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -24,11 +24,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xz-utils \ nodejs \ mingw-w64 \ - libgccjit-13-dev \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ && rm -rf /var/lib/apt/lists/* -# Note: libgccjit needs to match the default gcc version for the linker to find it. - # Install powershell (universal package) so we can test x.ps1 on Linux # FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ @@ -50,6 +53,10 @@ ENV RUST_CONFIGURE_ARGS \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ +COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ +COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +RUN /scripts/build-gccjit.sh /scripts + +COPY scripts/x86_64-gnu-llvm.sh /tmp/script.sh ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/script.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh similarity index 100% rename from src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/script.sh rename to src/ci/docker/scripts/x86_64-gnu-llvm.sh diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index bc81b1e04a7..972ef359337 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -357,7 +357,7 @@ jobs: - name: mingw-check-tidy <<: *job-linux-4c - - name: x86_64-gnu-llvm-16 + - name: x86_64-gnu-llvm-17 env: ENABLE_GCC_CODEGEN: "1" <<: *job-linux-16c @@ -520,11 +520,6 @@ jobs: RUST_BACKTRACE: 1 <<: *job-linux-8c - - name: x86_64-gnu-llvm-16 - env: - RUST_BACKTRACE: 1 - <<: *job-linux-8c - - name: x86_64-gnu-nopt <<: *job-linux-4c diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh index e3f76744cbe..2ae78235604 100755 --- a/src/ci/scripts/install-msys2.sh +++ b/src/ci/scripts/install-msys2.sh @@ -28,16 +28,19 @@ if isWindows; then # Install pacboy for easily installing packages pacman -S --noconfirm pactoys - # Delete these pre-installed tools so we can't accidentally use them, because we are using the - # MSYS2 setup action versions instead. - # Delete pre-installed version of MSYS2 - echo "Cleaning up tools in PATH" - rm -r "/c/msys64/" - # Delete Strawberry Perl, which contains a version of mingw - rm -r "/c/Strawberry/" - # Delete these other copies of mingw, I don't even know where they come from. - rm -r "/c/mingw64/" - rm -r "/c/mingw32/" + # Remove these pre-installed tools so we can't accidentally use them, because we are using the + # MSYS2 setup action versions instead. Because `rm -r`-ing them is slow, we mv them off path + # instead. + # Remove pre-installed version of MSYS2 + echo "Cleaning up existing tools in PATH" + notpath="/c/NOT/ON/PATH/" + mkdir --parents "$notpath" + mv -t "$notpath" "/c/msys64/" + # Remove Strawberry Perl, which contains a version of mingw + mv -t "$notpath" "/c/Strawberry/" + # Remove these other copies of mingw, I don't even know where they come from. + mv -t "$notpath" "/c/mingw64/" + mv -t "$notpath" "/c/mingw32/" echo "Finished cleaning up tools in PATH" if isKnownToBeMingwBuild; then diff --git a/src/doc/rust.css b/src/doc/rust.css index bd2e0b94518..9e51f03085f 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -136,6 +136,28 @@ h1 a:link, h1 a:visited, h2 a:link, h2 a:visited, h3 a:link, h3 a:visited, h4 a:link, h4 a:visited, h5 a:link, h5 a:visited {color: black;} +h1, h2, h3, h4, h5 { + /* This is needed to be able to position the doc-anchor. Ideally there + would be a
around the whole document, but we don't have that. */ + position: relative; +} + +a.doc-anchor { + color: black; + display: none; + position: absolute; + left: -20px; + /* We add this padding so that when the cursor moves from the heading's text to the anchor, + the anchor doesn't disappear. */ + padding-right: 5px; + /* And this padding is used to make the anchor larger and easier to click on. */ + padding-left: 3px; +} +*:hover > .doc-anchor { + display: block; +} + + /* Code */ pre, code { diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 12a421f3c45..83acce80f96 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -52,7 +52,7 @@ - [powerpc64-ibm-aix](platform-support/aix.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - - [riscv32*-unknown-none-elf](platform-support/riscv32imac-unknown-none-elf.md) + - [riscv32*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 2ebbf5e15e6..274745b9082 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -118,6 +118,7 @@ The `std` column in the table below has the following meanings: * ✓ indicates the full standard library is available. * \* indicates the target only supports [`no_std`] development. +* ? indicates the standard library support is unknown or a work-in-progress. [`no_std`]: https://rust-embedded.github.io/book/intro/no-std.html @@ -140,7 +141,7 @@ target | std | notes [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | ARM64 OpenHarmony `aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat `aarch64-unknown-none` | * | Bare ARM64, hardfloat -[`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | * | ARM64 UEFI +[`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI [`arm-linux-androideabi`](platform-support/android.md) | ✓ | ARMv6 Android `arm-unknown-linux-musleabi` | ✓ | ARMv6 Linux with musl 1.2.3 `arm-unknown-linux-musleabihf` | ✓ | ARMv6 Linux with musl 1.2.3, hardfloat @@ -162,15 +163,15 @@ target | std | notes [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] -[`riscv32imac-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) -[`riscv32i-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32I ISA) -[`riscv32im-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | | Bare RISC-V (RV32IM ISA) -[`riscv32imc-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) -[`riscv32imafc-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) +[`riscv32imac-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) +[`riscv32i-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32I ISA) +[`riscv32im-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IM ISA) +[`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) +[`riscv32imafc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4, glibc 2.23) @@ -199,7 +200,7 @@ target | std | notes [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat `x86_64-unknown-redox` | ✓ | Redox OS -[`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | * | 64-bit UEFI +[`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. [wasi-rename]: https://github.com/rust-lang/compiler-team/issues/607 diff --git a/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md similarity index 100% rename from src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md rename to src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md deleted file mode 100644 index edfe07fc053..00000000000 --- a/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md deleted file mode 100644 index edfe07fc053..00000000000 --- a/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md deleted file mode 100644 index edfe07fc053..00000000000 --- a/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md deleted file mode 100644 index edfe07fc053..00000000000 --- a/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index dfc026fe50b..855fb132fc8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1572,7 +1572,7 @@ fn first_non_private<'tcx>( path: &hir::Path<'tcx>, ) -> Option { let target_def_id = path.res.opt_def_id()?; - let (parent_def_id, ident) = match &path.segments[..] { + let (parent_def_id, ident) = match &path.segments { [] => return None, // Relative paths are available in the same scope as the owner. [leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident), diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 0b20cca3bca..365d63d9657 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -598,7 +598,7 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool { /// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable. pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL"); pub(crate) static DOC_CHANNEL: Lazy<&'static str> = - Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').filter(|c| !c.is_empty()).next().unwrap()); + Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').find(|c| !c.is_empty()).unwrap()); /// Render a sequence of macro arms in a format suitable for displaying to the user /// as part of an item declaration. diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6c5040414bc..f1887684797 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -150,13 +150,13 @@ impl RenderType { string.push('{'); write_optional_id(self.id, string); string.push('{'); - for generic in &self.generics.as_ref().map(Vec::as_slice).unwrap_or_default()[..] { + for generic in &self.generics.as_deref().unwrap_or_default()[..] { generic.write_to_string(string); } string.push('}'); if self.bindings.is_some() { string.push('{'); - for binding in &self.bindings.as_ref().map(Vec::as_slice).unwrap_or_default()[..] { + for binding in &self.bindings.as_deref().unwrap_or_default()[..] { string.push('{'); binding.0.write_to_string(string); string.push('{'); diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs index 451a44cd53a..524795ed77c 100644 --- a/src/librustdoc/passes/check_custom_code_classes.rs +++ b/src/librustdoc/passes/check_custom_code_classes.rs @@ -75,6 +75,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) sym::custom_code_classes_in_docs, GateIssue::Language, false, + None, ); err.note( diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index ea73d9afa2e..cebd2385656 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -290,14 +290,14 @@ impl NonCopyConst { promoted: None, }; let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); - let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None); + let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, rustc_span::DUMMY_SP); self.is_value_unfrozen_raw(cx, result, ty) } fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let args = cx.typeck_results().node_args(hir_id); - let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None); + let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), rustc_span::DUMMY_SP); self.is_value_unfrozen_raw(cx, result, ty) } @@ -305,7 +305,7 @@ impl NonCopyConst { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) { Ok(Some(instance)) => { @@ -315,8 +315,8 @@ impl NonCopyConst { }; tcx.const_eval_global_id_for_typeck(param_env, cid, span) }, - Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))), - Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))), + Ok(None) => Err(ErrorHandled::TooGeneric(span)), + Err(err) => Err(ErrorHandled::Reported(err.into(), span)), } } } diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 07ed4fbbf8e..e75d5953fae 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -550,7 +550,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let result = self .lcx .tcx - .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) + .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; let result = mir_to_const(self.lcx, result)?; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index f427055728f..83c595ce241 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3734,36 +3734,42 @@ impl<'test> TestCx<'test> { debug!(?support_lib_deps); debug!(?support_lib_deps_deps); - let res = self.cmd2procres( - Command::new(&self.config.rustc_path) - .arg("-o") - .arg(&recipe_bin) - .arg(format!( - "-Ldependency={}", - &support_lib_path.parent().unwrap().to_string_lossy() - )) - .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy())) - .arg("--extern") - .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) - .arg(&self.testpaths.file.join("rmake.rs")) - .env("TARGET", &self.config.target) - .env("PYTHON", &self.config.python) - .env("S", &src_root) - .env("RUST_BUILD_STAGE", &self.config.stage_id) - .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &tmpdir) - .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) - .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) - .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"), - ); + let mut cmd = Command::new(&self.config.rustc_path); + cmd.arg("-o") + .arg(&recipe_bin) + .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy())) + .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy())) + .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy())) + .arg("--extern") + .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) + .arg(&self.testpaths.file.join("rmake.rs")) + .env("TARGET", &self.config.target) + .env("PYTHON", &self.config.python) + .env("S", &src_root) + .env("RUST_BUILD_STAGE", &self.config.stage_id) + .env("RUSTC", cwd.join(&self.config.rustc_path)) + .env("TMPDIR", &tmpdir) + .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) + .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) + .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) + .env("LLVM_COMPONENTS", &self.config.llvm_components) + // We for sure don't want these tests to run in parallel, so make + // sure they don't have access to these vars if we run via `make` + // at the top level + .env_remove("MAKEFLAGS") + .env_remove("MFLAGS") + .env_remove("CARGO_MAKEFLAGS"); + + if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { + let mut stage0_sysroot = build_root.clone(); + stage0_sysroot.push("stage0-sysroot"); + debug!(?stage0_sysroot); + debug!(exists = stage0_sysroot.exists()); + + cmd.arg("--sysroot").arg(&stage0_sysroot); + } + + let res = self.cmd2procres(&mut cmd); if !res.status.success() { self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res); } diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 0f7200fb407..8d76a488269 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -5,7 +5,7 @@ use std::num::NonZero; use smallvec::SmallVec; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_middle::{mir::RetagKind, ty::Ty}; +use rustc_middle::mir::RetagKind; use rustc_target::abi::Size; use crate::*; @@ -291,19 +291,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn retag_box_to_raw( - &mut self, - val: &ImmTy<'tcx, Provenance>, - alloc_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { - let this = self.eval_context_mut(); - let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; - match method { - BorrowTrackerMethod::StackedBorrows => this.sb_retag_box_to_raw(val, alloc_ty), - BorrowTrackerMethod::TreeBorrows => this.tb_retag_box_to_raw(val, alloc_ty), - } - } - fn retag_place_contents( &mut self, kind: RetagKind, diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 9130601bbdd..7a6a85a2f79 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -865,24 +865,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.sb_retag_reference(val, new_perm, RetagInfo { cause, in_field: false }) } - fn sb_retag_box_to_raw( - &mut self, - val: &ImmTy<'tcx, Provenance>, - alloc_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { - let this = self.eval_context_mut(); - let is_global_alloc = alloc_ty.ty_adt_def().is_some_and(|adt| { - let global_alloc = this.tcx.require_lang_item(rustc_hir::LangItem::GlobalAlloc, None); - adt.did() == global_alloc - }); - if is_global_alloc { - // Retag this as-if it was a mutable reference. - this.sb_retag_ptr_value(RetagKind::Raw, val) - } else { - Ok(val.clone()) - } - } - fn sb_retag_place_contents( &mut self, kind: RetagKind, @@ -891,9 +873,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields; let retag_cause = match kind { - RetagKind::Raw | RetagKind::TwoPhase { .. } => unreachable!(), // these can only happen in `retag_ptr_value` + RetagKind::TwoPhase { .. } => unreachable!(), // can only happen in `retag_ptr_value` RetagKind::FnEntry => RetagCause::FnEntry, - RetagKind::Default => RetagCause::Normal, + RetagKind::Default | RetagKind::Raw => RetagCause::Normal, }; let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, retag_fields, in_field: false }; @@ -959,14 +941,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Check the type of this value to see what to do with it (retag, or recurse). match place.layout.ty.kind() { - ty::Ref(..) => { - let new_perm = - NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx); - self.retag_ptr_inplace(place, new_perm)?; - } - ty::RawPtr(..) => { - // We do *not* want to recurse into raw pointers -- wide raw pointers have - // fields, and for dyn Trait pointees those can have reference type! + ty::Ref(..) | ty::RawPtr(..) => { + if matches!(place.layout.ty.kind(), ty::Ref(..)) + || self.kind == RetagKind::Raw + { + let new_perm = + NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx); + self.retag_ptr_inplace(place, new_perm)?; + } } ty::Adt(adt, _) if adt.is_box() => { // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 9eb78b08ef7..80bdcbb7559 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -392,15 +392,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn tb_retag_box_to_raw( - &mut self, - val: &ImmTy<'tcx, Provenance>, - _alloc_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { - // Casts to raw pointers are NOPs in Tree Borrows. - Ok(val.clone()) - } - /// Retag all pointers that are stored in this place. fn tb_retag_place_contents( &mut self, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7e5518392d8..3a4ab32e4ab 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1503,7 +1503,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { fn eval_mir_constant( ecx: &InterpCx<'mir, 'tcx, Self>, val: mir::Const<'tcx>, - span: Option, + span: Span, layout: Option>, eval: F, ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>> @@ -1511,7 +1511,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { F: Fn( &InterpCx<'mir, 'tcx, Self>, mir::Const<'tcx>, - Option, + Span, Option>, ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>, { diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index 976d4b4de55..d16d5d99e9c 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -140,18 +140,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?; } - "retag_box_to_raw" => { - let [ptr] = check_arg_count(args)?; - let alloc_ty = generic_args[1].expect_ty(); - - let val = this.read_immediate(ptr)?; - let new_val = if this.machine.borrow_tracker.is_some() { - this.retag_box_to_raw(&val, alloc_ty)? - } else { - val - }; - this.write_immediate(*new_val, dest)?; - } // We want to return either `true` or `false` at random, or else something like // ``` diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index ddddcdcebd2..c97a052f517 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -549,7 +549,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let index = generic_args[2] .expect_const() - .eval(*this.tcx, this.param_env(), Some(this.tcx.span)) + .eval(*this.tcx, this.param_env(), this.tcx.span) .unwrap() .unwrap_branch(); let index_len = index.len(); diff --git a/src/tools/miri/test-cargo-miri/tests/main.rs b/src/tools/miri/test-cargo-miri/tests/main.rs index bb94c8f3787..72224e29619 100644 --- a/src/tools/miri/test-cargo-miri/tests/main.rs +++ b/src/tools/miri/test-cargo-miri/tests/main.rs @@ -1,3 +1 @@ -#![feature(imported_main)] - use cargo_miri_test::main; diff --git a/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr b/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr index c26c7f397b0..867907e98e6 100644 --- a/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr @@ -6,7 +6,7 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information -help: was created by a SharedReadWrite retag at offsets [0x0..0x4] +help: was created by a Unique retag at offsets [0x0..0x4] --> $DIR/newtype_pair_retagging.rs:LL:CC | LL | let ptr = Box::into_raw(Box::new(0i32)); diff --git a/src/tools/miri/tests/fail/both_borrows/newtype_retagging.stack.stderr b/src/tools/miri/tests/fail/both_borrows/newtype_retagging.stack.stderr index ae54da70fe2..56715938e97 100644 --- a/src/tools/miri/tests/fail/both_borrows/newtype_retagging.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/newtype_retagging.stack.stderr @@ -6,7 +6,7 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information -help: was created by a SharedReadWrite retag at offsets [0x0..0x4] +help: was created by a Unique retag at offsets [0x0..0x4] --> $DIR/newtype_retagging.rs:LL:CC | LL | let ptr = Box::into_raw(Box::new(0i32)); diff --git a/src/tools/miri/tests/pass/imported_main.rs b/src/tools/miri/tests/pass/imported_main.rs index 32b39152f78..eb93cd11d38 100644 --- a/src/tools/miri/tests/pass/imported_main.rs +++ b/src/tools/miri/tests/pass/imported_main.rs @@ -1,5 +1,3 @@ -#![feature(imported_main)] - pub mod foo { pub fn mymain() { println!("Hello, world!"); diff --git a/src/tools/miri/tests/pass/main_fn.rs b/src/tools/miri/tests/pass/main_fn.rs index 3b84d1abe6f..4cdd034f30e 100644 --- a/src/tools/miri/tests/pass/main_fn.rs +++ b/src/tools/miri/tests/pass/main_fn.rs @@ -1,5 +1,3 @@ -#![feature(imported_main)] - mod foo { pub(crate) fn bar() {} } diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index ac536d0fdde..dc0a6c2d91f 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -36,7 +36,6 @@ jobs: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu code-target: linux-x64 - container: ubuntu:18.04 - os: ubuntu-20.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 @@ -63,14 +62,6 @@ jobs: with: fetch-depth: ${{ env.FETCH_DEPTH }} - - name: Install toolchain dependencies - if: matrix.container == 'ubuntu:18.04' - shell: bash - run: | - apt-get update && apt-get install -y build-essential curl - curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y - echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH - - name: Install Rust toolchain run: | rustup update --no-self-update stable diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 903141eee9a..68ed32391b7 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -71,6 +71,7 @@ version = "0.0.0" dependencies = [ "cfg", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lz4_flex", "rustc-hash", "salsa", "semver", @@ -134,9 +135,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg" @@ -874,9 +875,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -992,6 +993,12 @@ dependencies = [ "url", ] +[[package]] +name = "lz4_flex" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" + [[package]] name = "mbe" version = "0.0.0" @@ -1597,6 +1604,7 @@ dependencies = [ "rayon", "rustc-hash", "scip", + "semver", "serde", "serde_json", "sourcegen", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 440f46a938b..0679522efd6 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -105,6 +105,10 @@ anyhow = "1.0.75" arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" +chalk-solve = { version = "0.96.0", default-features = false } +chalk-ir = "0.96.0" +chalk-recursive = { version = "0.96.0", default-features = false } +chalk-derive = "0.96.0" command-group = "2.0.1" crossbeam-channel = "0.5.8" dissimilar = "1.0.7" diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 118abf5d6eb..4ab99fc33c4 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -12,6 +12,8 @@ rust-version.workspace = true doctest = false [dependencies] +lz4_flex = { version = "0.11", default-features = false } + la-arena.workspace = true salsa.workspace = true rustc-hash.workspace = true diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs index 003ffb24d9d..f202a885e27 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -7,13 +7,13 @@ use salsa::Durability; use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; +use crate::{CrateGraph, SourceDatabaseExt, SourceDatabaseExt2, SourceRoot, SourceRootId}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] pub struct FileChange { pub roots: Option>, - pub files_changed: Vec<(FileId, Option>)>, + pub files_changed: Vec<(FileId, Option)>, pub crate_graph: Option, } @@ -42,7 +42,7 @@ impl FileChange { self.roots = Some(roots); } - pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + pub fn change_file(&mut self, file_id: FileId, new_text: Option) { self.files_changed.push((file_id, new_text)) } @@ -68,8 +68,8 @@ impl FileChange { let source_root = db.source_root(source_root_id); let durability = durability(&source_root); // XXX: can't actually remove the file, just reset the text - let text = text.unwrap_or_else(|| Arc::from("")); - db.set_file_text_with_durability(file_id, text, durability) + let text = text.unwrap_or_default(); + db.set_file_text_with_durability(file_id, &text, durability) } if let Some(crate_graph) = self.crate_graph { db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH); diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 758d2a45c8f..5dcb580723f 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,6 +7,7 @@ mod input; use std::panic; +use salsa::Durability; use syntax::{ast, Parse, SourceFile}; use triomphe::Arc; @@ -42,6 +43,7 @@ pub trait Upcast { fn upcast(&self) -> &T; } +pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16; pub const DEFAULT_PARSE_LRU_CAP: usize = 128; pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024; @@ -89,7 +91,10 @@ fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse { #[salsa::query_group(SourceDatabaseExtStorage)] pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] + fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>; + fn file_text(&self, file_id: FileId) -> Arc; + /// Path to a file, relative to the root of its source root. /// Source root of the file. #[salsa::input] @@ -101,6 +106,44 @@ pub trait SourceDatabaseExt: SourceDatabase { fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } +fn file_text(db: &dyn SourceDatabaseExt, file_id: FileId) -> Arc { + let bytes = db.compressed_file_text(file_id); + let bytes = + lz4_flex::decompress_size_prepended(&bytes).expect("lz4 decompression should not fail"); + let text = std::str::from_utf8(&bytes).expect("file contents should be valid UTF-8"); + Arc::from(text) +} + +pub trait SourceDatabaseExt2 { + fn set_file_text(&mut self, file_id: FileId, text: &str) { + self.set_file_text_with_durability(file_id, text, Durability::LOW); + } + + fn set_file_text_with_durability( + &mut self, + file_id: FileId, + text: &str, + durability: Durability, + ); +} + +impl SourceDatabaseExt2 for Db { + fn set_file_text_with_durability( + &mut self, + file_id: FileId, + text: &str, + durability: Durability, + ) { + let bytes = text.as_bytes(); + let compressed = lz4_flex::compress_prepend_size(bytes); + self.set_compressed_file_text_with_durability( + file_id, + Arc::from(compressed.as_slice()), + durability, + ) + } +} + fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<[CrateId]> { let graph = db.crate_graph(); let mut crates = graph diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index fbda065b10f..9b3a5026ac8 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -31,4 +31,4 @@ mbe.workspace = true syntax.workspace = true [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index 4be6ae7481d..b7dbb7b5fdd 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -47,6 +47,7 @@ impl CfgExpr { pub fn parse(tt: &tt::Subtree) -> CfgExpr { next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) } + /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option { match self { @@ -62,7 +63,6 @@ impl CfgExpr { } } } - fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { let name = match it.next() { None => return None, diff --git a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs index 6dac5899ee3..31378716b3e 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs @@ -28,19 +28,20 @@ pub enum CargoTestMessage { }, Suite, Finished, + Custom { + text: String, + }, } impl ParseFromLine for CargoTestMessage { - fn from_line(line: &str, error: &mut String) -> Option { + fn from_line(line: &str, _: &mut String) -> Option { let mut deserializer = serde_json::Deserializer::from_str(line); deserializer.disable_recursion_limit(); if let Ok(message) = CargoTestMessage::deserialize(&mut deserializer) { return Some(message); } - error.push_str(line); - error.push('\n'); - None + Some(CargoTestMessage::Custom { text: line.to_owned() }) } fn from_eof() -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 37d37fd3311..c9f1add2751 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -10,7 +10,6 @@ use std::ops::Index; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use either::Either; use hir_expand::{name::Name, HirFileId, InFile}; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; @@ -45,7 +44,8 @@ pub struct Body { /// /// If this `Body` is for the body of a constant, this will just be /// empty. - pub params: Vec, + pub params: Box<[PatId]>, + pub self_param: Option, /// The `ExprId` of the actual body expression. pub body_expr: ExprId, /// Block expressions in this body that may contain inner items. @@ -55,7 +55,7 @@ pub struct Body { pub type ExprPtr = AstPtr; pub type ExprSource = InFile; -pub type PatPtr = AstPtr>; +pub type PatPtr = AstPtr; pub type PatSource = InFile; pub type LabelPtr = AstPtr; @@ -63,6 +63,7 @@ pub type LabelSource = InFile; pub type FieldPtr = AstPtr; pub type FieldSource = InFile; + pub type PatFieldPtr = AstPtr; pub type PatFieldSource = InFile; @@ -88,6 +89,8 @@ pub struct BodySourceMap { label_map: FxHashMap, label_map_back: ArenaMap, + self_param: Option>>, + /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). /// Instead, we use id of expression (`92`) to identify the field. field_map_back: FxHashMap, @@ -215,10 +218,11 @@ impl Body { fn shrink_to_fit(&mut self) { let Self { body_expr: _, + params: _, + self_param: _, block_scopes, exprs, labels, - params, pats, bindings, binding_owners, @@ -226,7 +230,6 @@ impl Body { block_scopes.shrink_to_fit(); exprs.shrink_to_fit(); labels.shrink_to_fit(); - params.shrink_to_fit(); pats.shrink_to_fit(); bindings.shrink_to_fit(); binding_owners.shrink_to_fit(); @@ -297,6 +300,7 @@ impl Default for Body { params: Default::default(), block_scopes: Default::default(), binding_owners: Default::default(), + self_param: Default::default(), } } } @@ -354,14 +358,12 @@ impl BodySourceMap { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } - pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option { - let src = node.map(|it| AstPtr::new(it).wrap_left()); - self.pat_map.get(&src).cloned() + pub fn self_param_syntax(&self) -> Option>> { + self.self_param } - pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option { - let src = node.map(|it| AstPtr::new(it).wrap_right()); - self.pat_map.get(&src).cloned() + pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option { + self.pat_map.get(&node.map(AstPtr::new)).cloned() } pub fn label_syntax(&self, label: LabelId) -> LabelSource { @@ -401,6 +403,7 @@ impl BodySourceMap { fn shrink_to_fit(&mut self) { let Self { + self_param: _, expr_map, expr_map_back, pat_map, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 66691277894..340e95dbc2f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -4,7 +4,6 @@ use std::mem; use base_db::CrateId; -use either::Either; use hir_expand::{ name::{name, AsName, Name}, ExpandError, InFile, @@ -29,7 +28,6 @@ use crate::{ db::DefDatabase, expander::Expander, hir::{ - dummy_expr_id, format_args::{ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions, @@ -66,16 +64,7 @@ pub(super) fn lower( def_map: expander.module.def_map(db), source_map: BodySourceMap::default(), ast_id_map: db.ast_id_map(expander.current_file_id()), - body: Body { - exprs: Default::default(), - pats: Default::default(), - bindings: Default::default(), - binding_owners: Default::default(), - labels: Default::default(), - params: Vec::new(), - body_expr: dummy_expr_id(), - block_scopes: Vec::new(), - }, + body: Body::default(), expander, current_try_block_label: None, is_lowering_assignee_expr: false, @@ -191,35 +180,35 @@ impl ExprCollector<'_> { is_async_fn: bool, ) -> (Body, BodySourceMap) { if let Some((param_list, mut attr_enabled)) = param_list { + let mut params = vec![]; if let Some(self_param) = param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) { let is_mutable = self_param.mut_token().is_some() && self_param.amp_token().is_none(); - let ptr = AstPtr::new(&Either::Right(self_param)); let binding_id: la_arena::Idx = self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false)); - let param_pat = self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, ptr); - self.add_definition_to_binding(binding_id, param_pat); - self.body.params.push(param_pat); + self.body.self_param = Some(binding_id); + self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); } for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) { let param_pat = self.collect_pat_top(param.pat()); - self.body.params.push(param_pat); + params.push(param_pat); } + self.body.params = params.into_boxed_slice(); }; self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| { if is_async_fn { match body { Some(e) => { + let syntax_ptr = AstPtr::new(&e); let expr = this.collect_expr(e); - this.alloc_expr_desugared(Expr::Async { - id: None, - statements: Box::new([]), - tail: Some(expr), - }) + this.alloc_expr_desugared_with_ptr( + Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) }, + syntax_ptr, + ) } None => this.missing_expr(), } @@ -405,7 +394,7 @@ impl ExprCollector<'_> { } ast::Expr::ParenExpr(e) => { let inner = self.collect_expr_opt(e.expr()); - // make the paren expr point to the inner expression as well + // make the paren expr point to the inner expression as well for IDE resolution let src = self.expander.in_file(syntax_ptr); self.source_map.expr_map.insert(src, inner); inner @@ -707,6 +696,7 @@ impl ExprCollector<'_> { .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) }); let old_label = self.current_try_block_label.replace(label); + let ptr = AstPtr::new(&e).upcast(); let (btail, expr_id) = self.with_labeled_rib(label, |this| { let mut btail = None; let block = this.collect_block_(e, |id, statements, tail| { @@ -716,23 +706,21 @@ impl ExprCollector<'_> { (btail, block) }); - let callee = self.alloc_expr_desugared(Expr::Path(try_from_output)); + let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr); let next_tail = match btail { - Some(tail) => self.alloc_expr_desugared(Expr::Call { - callee, - args: Box::new([tail]), - is_assignee_expr: false, - }), + Some(tail) => self.alloc_expr_desugared_with_ptr( + Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false }, + ptr, + ), None => { - let unit = self.alloc_expr_desugared(Expr::Tuple { - exprs: Box::new([]), - is_assignee_expr: false, - }); - self.alloc_expr_desugared(Expr::Call { - callee, - args: Box::new([unit]), - is_assignee_expr: false, - }) + let unit = self.alloc_expr_desugared_with_ptr( + Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false }, + ptr, + ); + self.alloc_expr_desugared_with_ptr( + Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false }, + ptr, + ) } }; let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { @@ -1067,16 +1055,12 @@ impl ExprCollector<'_> { None => None, }, ); - match expansion { - Some(tail) => { - // Make the macro-call point to its expanded expression so we can query - // semantics on syntax pointers to the macro - let src = self.expander.in_file(syntax_ptr); - self.source_map.expr_map.insert(src, tail); - Some(tail) - } - None => None, - } + expansion.inspect(|&tail| { + // Make the macro-call point to its expanded expression so we can query + // semantics on syntax pointers to the macro + let src = self.expander.in_file(syntax_ptr); + self.source_map.expr_map.insert(src, tail); + }) } fn collect_stmt(&mut self, statements: &mut Vec, s: ast::Stmt) { @@ -1261,7 +1245,7 @@ impl ExprCollector<'_> { (Some(id), Pat::Bind { id, subpat }) }; - let ptr = AstPtr::new(&Either::Left(pat)); + let ptr = AstPtr::new(&pat); let pat = self.alloc_pat(pattern, ptr); if let Some(binding_id) = binding { self.add_definition_to_binding(binding_id, pat); @@ -1359,9 +1343,10 @@ impl ExprCollector<'_> { suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(), } } - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676 ast::Pat::LiteralPat(lit) => 'b: { - let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing }; + let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { + break 'b Pat::Missing; + }; let expr = Expr::Literal(hir_lit); let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); let expr_id = self.alloc_expr(expr, expr_ptr); @@ -1397,7 +1382,7 @@ impl ExprCollector<'_> { ast::Pat::MacroPat(mac) => match mac.macro_call() { Some(call) => { let macro_ptr = AstPtr::new(&call); - let src = self.expander.in_file(AstPtr::new(&Either::Left(pat))); + let src = self.expander.in_file(AstPtr::new(&pat)); let pat = self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { this.collect_pat_opt(expanded_pat, binding_list) @@ -1426,7 +1411,7 @@ impl ExprCollector<'_> { Pat::Range { start, end } } }; - let ptr = AstPtr::new(&Either::Left(pat)); + let ptr = AstPtr::new(&pat); self.alloc_pat(pattern, ptr) } @@ -1987,10 +1972,19 @@ impl ExprCollector<'_> { self.source_map.expr_map.insert(src, id); id } - // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow. + // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed. + // Migrate to alloc_expr_desugared_with_ptr and then rename back fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { self.body.exprs.alloc(expr) } + fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { + let src = self.expander.in_file(ptr); + let id = self.body.exprs.alloc(expr); + self.source_map.expr_map_back.insert(id, src); + // We intentionally don't fill this as it could overwrite a non-desugared entry + // self.source_map.expr_map.insert(src, id); + id + } fn missing_expr(&mut self) -> ExprId { self.alloc_expr_desugared(Expr::Missing) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index b2aab55a6a8..cbb5ca887f4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -48,7 +48,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); - body.params.iter().zip(db.function_data(it).params.iter()).for_each(|(¶m, ty)| { + let params = &db.function_data(it).params; + let mut params = params.iter(); + if let Some(self_param) = body.self_param { + p.print_binding(self_param); + p.buf.push(':'); + if let Some(ty) = params.next() { + p.print_type_ref(ty); + } + } + body.params.iter().zip(params).for_each(|(¶m, ty)| { p.print_pat(param); p.buf.push(':'); p.print_type_ref(ty); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs index 69b82ae871a..0020e4eac30 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs @@ -96,6 +96,9 @@ impl ExprScopes { scope_by_expr: ArenaMap::with_capacity(body.exprs.len()), }; let mut root = scopes.root_scope(); + if let Some(self_param) = body.self_param { + scopes.add_bindings(body, root, self_param); + } scopes.add_params_bindings(body, root, &body.params); compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); scopes diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs index f1c6b3b89fc..0b41984bdd8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs @@ -76,7 +76,7 @@ impl ChildBySource for ItemScope { self.extern_crate_decls() .for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::EXTERN_CRATE)); self.use_decls().for_each(|ext| insert_item_loc(db, res, file_id, ext, keys::USE)); - self.unnamed_consts(db) + self.unnamed_consts() .for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST)); self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each( |(ast_id, call_id)| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index d4c1db8b95b..b815c9b73ef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -715,7 +715,7 @@ impl<'a> AssocItemCollector<'a> { } AssocItem::MacroCall(call) => { let file_id = self.expander.current_file_id(); - let MacroCall { ast_id, expand_to, call_site, ref path } = item_tree[call]; + let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call]; let module = self.expander.module.local_id; let resolver = |path| { @@ -734,7 +734,7 @@ impl<'a> AssocItemCollector<'a> { match macro_call_as_call_id( self.db.upcast(), &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)), - call_site, + ctxt, expand_to, self.expander.module.krate(), resolver, @@ -745,6 +745,7 @@ impl<'a> AssocItemCollector<'a> { self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike { ast_id: InFile::new(file_id, ast_id), expand_to: hir_expand::ExpandTo::Items, + eager: None, }); } Ok(None) => (), @@ -754,6 +755,7 @@ impl<'a> AssocItemCollector<'a> { MacroCallKind::FnLike { ast_id: InFile::new(file_id, ast_id), expand_to, + eager: None, }, Clone::clone(path), )); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 5790e600f63..a7461b78af1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -191,9 +191,9 @@ impl StructData { let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); - let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { @@ -248,9 +248,9 @@ impl StructData { let krate = loc.container.krate; let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); - let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); + let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 544ed6bc347..30d52d87f19 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -309,13 +309,9 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()), local_inner: false, allow_internal_unsafe: loc.allow_internal_unsafe, - span: db - .span_map(loc.id.file_id()) - .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), edition: loc.edition, } } - MacroId::MacroRulesId(it) => { let loc: MacroRulesLoc = it.lookup(db); @@ -328,9 +324,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { allow_internal_unsafe: loc .flags .contains(MacroRulesLocFlags::ALLOW_INTERNAL_UNSAFE), - span: db - .span_map(loc.id.file_id()) - .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), edition: loc.edition, } } @@ -348,9 +341,6 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { ), local_inner: false, allow_internal_unsafe: false, - span: db - .span_map(loc.id.file_id()) - .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()), edition: loc.edition, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0e6826a75a6..2b059d1f8dc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -241,30 +241,8 @@ impl ItemScope { }) } - pub fn unnamed_consts<'a>( - &'a self, - db: &'a dyn DefDatabase, - ) -> impl Iterator + 'a { - // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those. - // Should be removed once synstructure stops doing that. - let synstructure_hack_consts = self.values.values().filter_map(|(item, _, _)| match item { - &ModuleDefId::ConstId(id) => { - let loc = id.lookup(db); - let item_tree = loc.id.item_tree(db); - if item_tree[loc.id.value] - .name - .as_ref() - .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) - { - Some(id) - } else { - None - } - } - _ => None, - }); - - self.unnamed_consts.iter().copied().chain(synstructure_hack_consts) + pub fn unnamed_consts(&self) -> impl Iterator + '_ { + self.unnamed_consts.iter().copied() } /// Iterate over all module scoped macros diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index bd3d377ec08..585e93ce21e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -49,7 +49,7 @@ use intern::Interned; use la_arena::{Arena, Idx, IdxRange, RawIdx}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use span::{AstIdNode, FileAstId, Span}; +use span::{AstIdNode, FileAstId, SyntaxContextId}; use stdx::never; use syntax::{ast, match_ast, SyntaxKind}; use triomphe::Arc; @@ -790,8 +790,7 @@ pub struct MacroCall { pub path: Interned, pub ast_id: FileAstId, pub expand_to: ExpandTo, - // FIXME: We need to move this out. It invalidates the item tree when typing inside the macro call. - pub call_site: Span, + pub ctxt: SyntaxContextId, } #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index bf3d54f4caf..f02163cbe44 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -560,35 +560,32 @@ impl<'a> Ctx<'a> { fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option> { let span_map = self.span_map(); - let path = Interned::new(ModPath::from_src(self.db.upcast(), m.path()?, &mut |range| { + let path = m.path()?; + let range = path.syntax().text_range(); + let path = Interned::new(ModPath::from_src(self.db.upcast(), path, &mut |range| { span_map.span_for_range(range).ctx })?); 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, - call_site: span_map.span_for_range(m.syntax().text_range()), - }; + let res = MacroCall { path, ast_id, expand_to, ctxt: span_map.span_for_range(range).ctx }; Some(id(self.data().macro_calls.alloc(res))) } fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option> { - let name = m.name().map(|it| it.as_name())?; + let name = m.name()?; let ast_id = self.source_ast_id_map.ast_id(m); - let res = MacroRules { name, ast_id }; + let res = MacroRules { name: name.as_name(), ast_id }; Some(id(self.data().macro_rules.alloc(res))) } fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option> { - let name = m.name().map(|it| it.as_name())?; + 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, ast_id, visibility }; + let res = Macro2 { name: name.as_name(), ast_id, visibility }; Some(id(self.data().macro_defs.alloc(res))) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 87c90a4c6ab..953bf6b85d6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -487,12 +487,12 @@ impl Printer<'_> { } } ModItem::MacroCall(it) => { - let MacroCall { path, ast_id, expand_to, call_site } = &self.tree[it]; + let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it]; let _ = writeln!( self, - "// AstId: {:?}, Span: {}, ExpandTo: {:?}", + "// AstId: {:?}, SyntaxContext: {}, ExpandTo: {:?}", ast_id.erase().into_raw(), - call_site, + ctxt, expand_to ); wln!(self, "{}!(...);", path.display(self.db.upcast())); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs index 26f7b41c77a..48da876ac15 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs @@ -278,7 +278,7 @@ m!(); // AstId: 2 pub macro m2 { ... } - // AstId: 3, Span: 0:3@0..5#0, ExpandTo: Items + // AstId: 3, SyntaxContext: 0, ExpandTo: Items m!(...); "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index d63f2268aa4..828842de7e8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -90,7 +90,7 @@ use hir_expand::{ use item_tree::ExternBlock; use la_arena::Idx; use nameres::DefMap; -use span::{AstIdNode, FileAstId, FileId, Span}; +use span::{AstIdNode, FileAstId, FileId, SyntaxContextId}; use stdx::impl_from; use syntax::{ast, AstNode}; @@ -1342,21 +1342,22 @@ impl AsMacroCall for InFile<&ast::MacroCall> { let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); let span_map = db.span_map(self.file_id); let path = self.value.path().and_then(|path| { - path::ModPath::from_src(db, path, &mut |range| { + let range = path.syntax().text_range(); + let mod_path = path::ModPath::from_src(db, path, &mut |range| { span_map.as_ref().span_for_range(range).ctx - }) + })?; + let call_site = span_map.span_for_range(range); + Some((call_site, mod_path)) }); - let Some(path) = path else { + let Some((call_site, path)) = path else { return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation"))); }; - let call_site = span_map.span_for_range(self.value.syntax().text_range()); - macro_call_as_call_id_with_eager( db, &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), - call_site, + call_site.ctx, expands_to, krate, resolver, @@ -1381,7 +1382,7 @@ impl AstIdWithPath { fn macro_call_as_call_id( db: &dyn ExpandDatabase, call: &AstIdWithPath, - call_site: Span, + call_site: SyntaxContextId, expand_to: ExpandTo, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option + Copy, @@ -1393,7 +1394,7 @@ fn macro_call_as_call_id( fn macro_call_as_call_id_with_eager( db: &dyn ExpandDatabase, call: &AstIdWithPath, - call_site: Span, + call_site: SyntaxContextId, expand_to: ExpandTo, krate: CrateId, resolver: impl FnOnce(path::ModPath) -> Option, @@ -1403,17 +1404,20 @@ fn macro_call_as_call_id_with_eager( resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; let res = match def.kind { - MacroDefKind::BuiltInEager(..) => { - let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db)); - expand_eager_macro_input(db, krate, macro_call, def, call_site, &|path| { - eager_resolver(path).filter(MacroDefId::is_fn_like) - }) - } + MacroDefKind::BuiltInEager(..) => expand_eager_macro_input( + db, + krate, + &call.ast_id.to_node(db), + call.ast_id, + def, + call_site, + &|path| eager_resolver(path).filter(MacroDefId::is_fn_like), + ), _ if def.is_fn_like() => ExpandResult { - value: Some(def.as_lazy_macro( + value: Some(def.make_call( db, krate, - MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, + MacroCallKind::FnLike { ast_id: call.ast_id, expand_to, eager: None }, call_site, )), err: None, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 86b4466153a..89c1b446081 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -528,3 +528,121 @@ impl < > $crate::fmt::Debug for Command< > where { }"#]], ); } +#[test] +fn test_debug_expand_with_cfg() { + check( + r#" + //- minicore: derive, fmt + use core::fmt::Debug; + + #[derive(Debug)] + struct HideAndShow { + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, + } + #[derive(Debug)] + enum HideAndShowEnum { + #[cfg(never)] + AlwaysHide, + #[cfg(not(never))] + AlwaysShow{ + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, + } + } + "#, + expect![[r#" +use core::fmt::Debug; + +#[derive(Debug)] +struct HideAndShow { + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, +} +#[derive(Debug)] +enum HideAndShowEnum { + #[cfg(never)] + AlwaysHide, + #[cfg(not(never))] + AlwaysShow{ + #[cfg(never)] + always_hide: u32, + #[cfg(not(never))] + always_show: u32, + } +} + +impl < > $crate::fmt::Debug for HideAndShow< > where { + fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { + match self { + HideAndShow { + always_show: always_show, + } + =>f.debug_struct("HideAndShow").field("always_show", &always_show).finish() + } + } +} +impl < > $crate::fmt::Debug for HideAndShowEnum< > where { + fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { + match self { + HideAndShowEnum::AlwaysShow { + always_show: always_show, + } + =>f.debug_struct("AlwaysShow").field("always_show", &always_show).finish(), + } + } +}"#]], + ); +} +#[test] +fn test_default_expand_with_cfg() { + check( + r#" +//- minicore: derive, default +#[derive(Default)] +struct Foo { + field1: i32, + #[cfg(never)] + field2: (), +} +#[derive(Default)] +enum Bar { + Foo, + #[cfg_attr(not(never), default)] + Bar, +} +"#, + expect![[r#" +#[derive(Default)] +struct Foo { + field1: i32, + #[cfg(never)] + field2: (), +} +#[derive(Default)] +enum Bar { + Foo, + #[cfg_attr(not(never), default)] + Bar, +} + +impl < > $crate::default::Default for Foo< > where { + fn default() -> Self { + Foo { + field1: $crate::default::Default::default(), + } + } +} +impl < > $crate::default::Default for Bar< > where { + fn default() -> Self { + Bar::Bar + } +}"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index edc8247f166..965f329acb9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -171,7 +171,7 @@ fn main(foo: ()) { } fn main(foo: ()) { - /* error: unresolved macro unresolved */"helloworld!"#0:3@207..323#2#; + /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#0#; } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs index 63f211022c9..23d8b023b8b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs @@ -33,7 +33,7 @@ m!(&k"); "#, expect![[r#" macro_rules! m { ($i:literal) => {}; } -/* error: mismatched delimiters */"#]], +/* error: expected literal */"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs index 362c189f6a7..fb5797d6e53 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } } macro_rules! m2 { ($x:ident) => {} } /* error: macro definition has parse errors */ -/* error: mismatched delimiters */ +/* error: expected ident */ "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 764617eafb7..b56dee3efb5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -61,15 +61,16 @@ use std::ops::Deref; use base_db::{CrateId, Edition, FileId}; use hir_expand::{ - name::Name, proc_macro::ProcMacroKind, HirFileId, InFile, MacroCallId, MacroDefId, + name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, }; use itertools::Itertools; use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; -use span::FileAstId; +use span::{FileAstId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{ast, SmolStr}; use triomphe::Arc; +use tt::TextRange; use crate::{ db::DefDatabase, @@ -677,6 +678,25 @@ impl ModuleData { } } + pub fn definition_source_range(&self, db: &dyn DefDatabase) -> InFile { + match &self.origin { + &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { + InFile::new( + definition.into(), + ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID) + .to_range(db.upcast()), + ) + } + &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( + definition_tree_id.file_id(), + AstId::new(definition_tree_id.file_id(), definition).to_range(db.upcast()), + ), + ModuleOrigin::BlockExpr { block, .. } => { + InFile::new(block.file_id, block.to_range(db.upcast())) + } + } + } + /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. /// `None` for the crate root or block. pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option> { @@ -684,6 +704,13 @@ impl ModuleData { let value = decl.to_node(db.upcast()); Some(InFile { file_id: decl.file_id, value }) } + + /// Returns the range which declares this module, either a `mod foo;` or a `mod foo {}`. + /// `None` for the crate root or block. + pub fn declaration_source_range(&self, db: &dyn DefDatabase) -> Option> { + let decl = self.origin.declaration()?; + Some(InFile { file_id: decl.file_id, value: decl.to_range(db.upcast()) }) + } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 1cadae8c87c..662c80edf32 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -5,7 +5,7 @@ use hir_expand::{ attrs::{Attr, AttrId, AttrInput}, MacroCallId, MacroCallKind, MacroDefId, }; -use span::Span; +use span::SyntaxContextId; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -109,14 +109,14 @@ pub(super) fn attr_macro_as_call_id( let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt)) => { let mut tt = tt.as_ref().clone(); - tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span); + tt.delimiter.kind = tt::DelimiterKind::Invisible; Some(tt) } _ => None, }; - def.as_lazy_macro( + def.make_call( db.upcast(), krate, MacroCallKind::Attr { @@ -124,7 +124,7 @@ pub(super) fn attr_macro_as_call_id( attr_args: arg.map(Arc::new), invoc_attr_index: macro_attr.id, }, - macro_attr.span, + macro_attr.ctxt, ) } @@ -133,14 +133,14 @@ pub(super) fn derive_macro_as_call_id( item_attr: &AstIdWithPath, derive_attr_index: AttrId, derive_pos: u32, - call_site: Span, + call_site: SyntaxContextId, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { let (macro_id, def_id) = resolver(item_attr.path.clone()) .filter(|(_, def_id)| def_id.is_derive()) .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; - let call_id = def_id.as_lazy_macro( + let call_id = def_id.make_call( db.upcast(), krate, MacroCallKind::Derive { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index f9fe6d3b903..3d026447fb7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -230,13 +230,13 @@ enum MacroDirectiveKind { FnLike { ast_id: AstIdWithPath, expand_to: ExpandTo, - call_site: Span, + ctxt: SyntaxContextId, }, Derive { ast_id: AstIdWithPath, derive_attr: AttrId, derive_pos: usize, - call_site: Span, + ctxt: SyntaxContextId, }, Attr { ast_id: AstIdWithPath, @@ -1126,7 +1126,7 @@ impl DefCollector<'_> { let resolver_def_id = |path| resolver(path).map(|(_, it)| it); match &directive.kind { - MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => { + MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => { let call_id = macro_call_as_call_id( self.db.upcast(), ast_id, @@ -1146,7 +1146,7 @@ impl DefCollector<'_> { return Resolved::Yes; } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => { + MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => { let id = derive_macro_as_call_id( self.db, ast_id, @@ -1266,7 +1266,7 @@ impl DefCollector<'_> { ast_id, derive_attr: attr.id, derive_pos: idx, - call_site, + ctxt: call_site.ctx, }, container: directive.container, }); @@ -1428,7 +1428,7 @@ impl DefCollector<'_> { for directive in &self.unresolved_macros { match &directive.kind { - MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => { + MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => { // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error! let macro_call_as_call_id = macro_call_as_call_id( self.db.upcast(), @@ -1451,12 +1451,16 @@ impl DefCollector<'_> { if let Err(UnresolvedMacro { path }) = macro_call_as_call_id { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, - MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: *expand_to }, + MacroCallKind::FnLike { + ast_id: ast_id.ast_id, + expand_to: *expand_to, + eager: None, + }, path, )); } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site: _ } => { + MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, MacroCallKind::Derive { @@ -2285,7 +2289,7 @@ impl ModCollector<'_, '_> { fn collect_macro_call( &mut self, - &MacroCall { ref path, ast_id, expand_to, call_site }: &MacroCall, + &MacroCall { ref path, ast_id, expand_to, ctxt }: &MacroCall, container: ItemContainerId, ) { let ast_id = AstIdWithPath::new(self.file_id(), ast_id, ModPath::clone(path)); @@ -2299,7 +2303,7 @@ impl ModCollector<'_, '_> { if let Ok(res) = macro_call_as_call_id_with_eager( db.upcast(), &ast_id, - call_site, + ctxt, expand_to, self.def_collector.def_map.krate, |path| { @@ -2357,7 +2361,7 @@ impl ModCollector<'_, '_> { self.def_collector.unresolved_macros.push(MacroDirective { module_id: self.module_id, depth: self.macro_depth + 1, - kind: MacroDirectiveKind::FnLike { ast_id, expand_to, call_site }, + kind: MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt }, container, }); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 6efced02718..be41634eb57 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -1,6 +1,5 @@ -use base_db::{SourceDatabase, SourceDatabaseExt}; +use base_db::{SourceDatabase, SourceDatabaseExt2 as _}; use test_fixture::WithFixture; -use triomphe::Arc; use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId}; @@ -17,7 +16,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: }); assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}") } - db.set_file_text(pos.file_id, Arc::from(ra_fixture_change)); + db.set_file_text(pos.file_id, ra_fixture_change); { let events = db.log_executed(|| { @@ -267,7 +266,7 @@ fn quux() { 92 } m!(Y); m!(Z); "#; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); { let events = db.log_executed(|| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index 4283f003f89..2b1da8c34e1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -3,7 +3,7 @@ use either::Either; use hir_expand::InFile; use la_arena::ArenaMap; -use syntax::ast; +use syntax::{ast, AstNode, AstPtr}; use crate::{ data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId, @@ -12,8 +12,12 @@ use crate::{ }; pub trait HasSource { - type Value; - fn source(&self, db: &dyn DefDatabase) -> InFile; + type Value: AstNode; + fn source(&self, db: &dyn DefDatabase) -> InFile { + let InFile { file_id, value } = self.ast_ptr(db); + InFile::new(file_id, value.to_node(&db.parse_or_expand(file_id))) + } + fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile>; } impl HasSource for T @@ -22,16 +26,14 @@ where T::Id: ItemTreeNode, { type Value = ::Source; - - fn source(&self, db: &dyn DefDatabase) -> InFile { + fn ast_ptr(&self, db: &dyn DefDatabase) -> InFile> { 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 root = db.parse_or_expand(file_id); let node = &tree[id.value]; - InFile::new(file_id, ast_id_map.get(node.ast_id()).to_node(&root)) + InFile::new(file_id, ast_id_map.get(node.ast_id())) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 7793e995323..af3ecdcd5e3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -7,7 +7,7 @@ use either::Either; use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; -use span::Span; +use span::{Span, SyntaxContextId}; use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use triomphe::Arc; @@ -53,7 +53,7 @@ impl RawAttrs { id, input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), path: Interned::new(ModPath::from(crate::name!(doc))), - span: span_map.span_for_range(comment.syntax().text_range()), + ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, }), }); let entries: Arc<[Attr]> = Arc::from_iter(entries); @@ -173,7 +173,7 @@ pub struct Attr { pub id: AttrId, pub path: Interned, pub input: Option>, - pub span: Span, + pub ctxt: SyntaxContextId, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -201,10 +201,12 @@ impl Attr { span_map: SpanMapRef<'_>, id: AttrId, ) -> Option { - let path = Interned::new(ModPath::from_src(db, ast.path()?, &mut |range| { + let path = ast.path()?; + let range = path.syntax().text_range(); + let path = Interned::new(ModPath::from_src(db, path, &mut |range| { span_map.span_for_range(range).ctx })?); - let span = span_map.span_for_range(ast.syntax().text_range()); + let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { let value = match lit.kind() { ast::LiteralKind::String(string) => string.value()?.into(), @@ -217,11 +219,11 @@ impl Attr { } else { None }; - Some(Attr { id, path, input, span }) + Some(Attr { id, path, input, ctxt: span.ctx }) } fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option { - let span = tt.first()?.first_span(); + let ctxt = tt.first()?.first_span().ctx; let path_end = tt .iter() .position(|tt| { @@ -253,7 +255,7 @@ impl Attr { } _ => None, }; - Some(Attr { id, path, input, span }) + Some(Attr { id, path, input, ctxt }) } pub fn path(&self) -> &ModPath { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs index a0102f36aff..9ff29b484d3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs @@ -11,7 +11,7 @@ macro_rules! register_builtin { } impl BuiltinAttrExpander { - pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree) -> ExpandResult { + pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult { match *self { $( BuiltinAttrExpander::$variant => $expand, )* } @@ -34,8 +34,9 @@ impl BuiltinAttrExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - self.expander()(db, id, tt) + self.expander()(db, id, tt, span) } pub fn is_derive(self) -> bool { @@ -71,6 +72,7 @@ fn dummy_attr_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, + _span: Span, ) -> ExpandResult { ExpandResult::ok(tt.clone()) } @@ -100,6 +102,7 @@ fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let derives = match &loc.kind { @@ -107,17 +110,14 @@ fn derive_expand( attr_args } _ => { - return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { - open: loc.call_site, - close: loc.call_site, - })) + return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { open: span, close: span })) } }; - pseudo_derive_attr_expansion(tt, derives, loc.call_site) + pseudo_derive_attr_expansion(tt, derives, span) } pub fn pseudo_derive_attr_expansion( - tt: &tt::Subtree, + _: &tt::Subtree, args: &tt::Subtree, call_site: Span, ) -> ExpandResult { @@ -141,7 +141,7 @@ pub fn pseudo_derive_attr_expansion( token_trees.push(mk_leaf(']')); } ExpandResult::ok(tt::Subtree { - delimiter: tt.delimiter, + delimiter: args.delimiter, token_trees: token_trees.into_boxed_slice(), }) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index 66dec7d89e5..528038a9ccf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -50,8 +50,8 @@ impl BuiltinDeriveExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); self.expander()(span, tt) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 0fd0c25dcce..9fb6a0b2346 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -19,14 +19,14 @@ use crate::{ }; macro_rules! register_builtin { - ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { + ( $LAZY:ident: $(($name:ident, $kind: ident) => $expand:ident),* , $EAGER:ident: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum BuiltinFnLikeExpander { + pub enum $LAZY { $($kind),* } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub enum EagerExpander { + pub enum $EAGER { $($e_kind),* } @@ -62,8 +62,8 @@ impl BuiltinFnLikeExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -75,8 +75,8 @@ impl EagerExpander { db: &dyn ExpandDatabase, id: MacroCallId, tt: &tt::Subtree, + span: Span, ) -> ExpandResult { - let span = db.lookup_intern_macro_call(id).call_site; let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -84,6 +84,17 @@ impl EagerExpander { pub fn is_include(&self) -> bool { matches!(self, EagerExpander::Include) } + + pub fn is_include_like(&self) -> bool { + matches!( + self, + EagerExpander::Include | EagerExpander::IncludeStr | EagerExpander::IncludeBytes + ) + } + + pub fn is_env_or_option_env(&self) -> bool { + matches!(self, EagerExpander::Env | EagerExpander::OptionEnv) + } } pub fn find_builtin_macro( @@ -93,7 +104,7 @@ pub fn find_builtin_macro( } register_builtin! { - LAZY: + BuiltinFnLikeExpander: (column, Column) => line_expand, (file, File) => file_expand, (line, Line) => line_expand, @@ -114,7 +125,7 @@ register_builtin! { (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, - EAGER: + EagerExpander: (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, (concat_idents, ConcatIdents) => concat_idents_expand, @@ -426,22 +437,25 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { } } -fn unquote_str(lit: &tt::Literal) -> Option { +fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> { + let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::String::cast(lit)?; - token.value().map(|it| it.into_owned()) + token.value().map(|it| (it.into_owned(), span)) } -fn unquote_char(lit: &tt::Literal) -> Option { +fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> { + let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::Char::cast(lit)?; - token.value() + token.value().zip(Some(span)) } -fn unquote_byte_string(lit: &tt::Literal) -> Option> { +fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec, Span)> { + let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::ByteString::cast(lit)?; - token.value().map(|it| it.into_owned()) + token.value().map(|it| (it.into_owned(), span)) } fn compile_error_expand( @@ -452,7 +466,7 @@ fn compile_error_expand( ) -> ExpandResult { let err = match &*tt.token_trees { [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) { - Some(unquoted) => ExpandError::other(unquoted.into_boxed_str()), + Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()), None => ExpandError::other("`compile_error!` argument must be a string"), }, _ => ExpandError::other("`compile_error!` argument must be a string"), @@ -465,10 +479,16 @@ fn concat_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: Span, + _: Span, ) -> ExpandResult { let mut err = None; let mut text = String::new(); + let mut span: Option = None; + let mut record_span = |s: Span| match &mut span { + Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range), + Some(_) => (), + None => span = Some(s), + }; for (i, mut t) in tt.token_trees.iter().enumerate() { // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd @@ -486,11 +506,14 @@ fn concat_expand( // concat works with string and char literals, so remove any quotes. // It also works with integer, float and boolean literals, so just use the rest // as-is. - if let Some(c) = unquote_char(it) { + if let Some((c, span)) = unquote_char(it) { text.push(c); + record_span(span); } else { - let component = unquote_str(it).unwrap_or_else(|| it.text.to_string()); + let (component, span) = + unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span)); text.push_str(&component); + record_span(span); } } // handle boolean literals @@ -498,6 +521,7 @@ fn concat_expand( if i % 2 == 0 && (id.text == "true" || id.text == "false") => { text.push_str(id.text.as_str()); + record_span(id.span); } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { @@ -505,6 +529,7 @@ fn concat_expand( } } } + let span = span.unwrap_or(tt.delimiter.open); ExpandResult { value: quote!(span =>#text), err } } @@ -512,18 +537,25 @@ fn concat_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, - span: Span, + call_site: Span, ) -> ExpandResult { let mut bytes = Vec::new(); let mut err = None; + let mut span: Option = None; + let mut record_span = |s: Span| match &mut span { + Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range), + Some(_) => (), + None => span = Some(s), + }; for (i, t) in tt.token_trees.iter().enumerate() { match t { tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { let token = ast::make::tokens::literal(&lit.to_string()); + record_span(lit.span); match token.kind() { syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()), syntax::SyntaxKind::BYTE_STRING => { - let components = unquote_byte_string(lit).unwrap_or_default(); + let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it); components.into_iter().for_each(|it| bytes.push(it.to_string())); } _ => { @@ -534,7 +566,7 @@ fn concat_bytes_expand( } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => { - if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) { + if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span) { err.get_or_insert(e); break; } @@ -546,17 +578,24 @@ fn concat_bytes_expand( } } let value = tt::Subtree { - delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket }, + delimiter: tt::Delimiter { + open: call_site, + close: call_site, + kind: tt::DelimiterKind::Bracket, + }, token_trees: { Itertools::intersperse_with( bytes.into_iter().map(|it| { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: it.into(), span })) + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + text: it.into(), + span: span.unwrap_or(call_site), + })) }), || { tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', spacing: tt::Spacing::Alone, - span, + span: call_site, })) }, ) @@ -569,13 +608,15 @@ fn concat_bytes_expand( fn concat_bytes_expand_subtree( tree: &tt::Subtree, bytes: &mut Vec, + mut record_span: impl FnMut(Span), ) -> Result<(), ExpandError> { for (ti, tt) in tree.token_trees.iter().enumerate() { match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - let lit = ast::make::tokens::literal(&lit.to_string()); + tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => { + let lit = ast::make::tokens::literal(&it.to_string()); match lit.kind() { syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => { + record_span(it.span); bytes.push(lit.text().to_owned()) } _ => { @@ -635,7 +676,7 @@ fn relative_file( } } -fn parse_string(tt: &tt::Subtree) -> Result { +fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> { tt.token_trees .first() .and_then(|tt| match tt { @@ -675,7 +716,7 @@ pub fn include_input_to_file_id( arg_id: MacroCallId, arg: &tt::Subtree, ) -> Result { - relative_file(db, arg_id, &parse_string(arg)?, false) + relative_file(db, arg_id, &parse_string(arg)?.0, false) } fn include_bytes_expand( @@ -701,7 +742,7 @@ fn include_str_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - let path = match parse_string(tt) { + let (path, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) @@ -736,7 +777,7 @@ fn env_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - let key = match parse_string(tt) { + let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) @@ -766,18 +807,24 @@ fn option_env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - span: Span, + call_site: Span, ) -> ExpandResult { - let key = match parse_string(tt) { + let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }), + e, + ) } }; - let dollar_crate = dollar_crate(span); + let dollar_crate = dollar_crate(call_site); let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! {span => #dollar_crate::option::Option::None::<&str> }, - Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) }, + None => quote! {call_site => #dollar_crate::option::Option::None::<&str> }, + Some(s) => { + let s = quote! (span => #s); + quote! {call_site => #dollar_crate::option::Option::Some(#s) } + } }; ExpandResult::ok(expanded) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs new file mode 100644 index 00000000000..c74c13a6fd3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -0,0 +1,327 @@ +//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro +use std::iter::Peekable; + +use cfg::{CfgAtom, CfgExpr}; +use rustc_hash::FxHashSet; +use syntax::{ + ast::{self, Attr, HasAttrs, Meta, VariantList}, + AstNode, NodeOrToken, SyntaxElement, SyntaxNode, T, +}; +use tracing::{debug, warn}; +use tt::SmolStr; + +use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc}; + +fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { + if !attr.simple_name().as_deref().map(|v| v == "cfg")? { + return None; + } + debug!("Evaluating cfg {}", attr); + let cfg = parse_from_attr_meta(attr.meta()?)?; + debug!("Checking cfg {:?}", cfg); + let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false); + Some(enabled) +} + +fn check_cfg_attr_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { + if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { + return None; + } + debug!("Evaluating cfg_attr {}", attr); + let cfg_expr = parse_from_attr_meta(attr.meta()?)?; + debug!("Checking cfg_attr {:?}", cfg_expr); + let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg_expr) != Some(false); + Some(enabled) +} + +fn process_has_attrs_with_possible_comma( + items: impl Iterator, + loc: &MacroCallLoc, + db: &dyn ExpandDatabase, + remove: &mut FxHashSet, +) -> Option<()> { + for item in items { + let field_attrs = item.attrs(); + 'attrs: for attr in field_attrs { + if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { + debug!("censoring type {:?}", item.syntax()); + remove.insert(item.syntax().clone().into()); + // We need to remove the , as well + remove_possible_comma(&item, remove); + break 'attrs; + } + + if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if enabled { + debug!("Removing cfg_attr tokens {:?}", attr); + let meta = attr.meta()?; + let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; + remove.extend(removes_from_cfg_attr); + } else { + debug!("censoring type cfg_attr {:?}", item.syntax()); + remove.insert(attr.syntax().clone().into()); + continue; + } + } + } + } + Some(()) +} +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum CfgExprStage { + /// Stripping the CFGExpr part of the attribute + StrippigCfgExpr, + /// Found the comma after the CFGExpr. Will keep all tokens until the next comma or the end of the attribute + FoundComma, + /// Everything following the attribute. This could be another attribute or the end of the attribute. + // FIXME: cfg_attr with multiple attributes will not be handled correctly. We will only keep the first attribute + // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110 + EverythingElse, +} +/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input. +fn remove_tokens_within_cfg_attr(meta: Meta) -> Option> { + let mut remove: FxHashSet = FxHashSet::default(); + debug!("Enabling attribute {}", meta); + let meta_path = meta.path()?; + debug!("Removing {:?}", meta_path.syntax()); + remove.insert(meta_path.syntax().clone().into()); + + let meta_tt = meta.token_tree()?; + debug!("meta_tt {}", meta_tt); + let mut stage = CfgExprStage::StrippigCfgExpr; + for tt in meta_tt.token_trees_and_tokens() { + debug!("Checking {:?}. Stage: {:?}", tt, stage); + match (stage, tt) { + (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Node(node)) => { + remove.insert(node.syntax().clone().into()); + } + (CfgExprStage::StrippigCfgExpr, syntax::NodeOrToken::Token(token)) => { + if token.kind() == T![,] { + stage = CfgExprStage::FoundComma; + } + remove.insert(token.into()); + } + (CfgExprStage::FoundComma, syntax::NodeOrToken::Token(token)) + if (token.kind() == T![,] || token.kind() == T![')']) => + { + // The end of the attribute or separator for the next attribute + stage = CfgExprStage::EverythingElse; + remove.insert(token.into()); + } + (CfgExprStage::EverythingElse, syntax::NodeOrToken::Node(node)) => { + remove.insert(node.syntax().clone().into()); + } + (CfgExprStage::EverythingElse, syntax::NodeOrToken::Token(token)) => { + remove.insert(token.into()); + } + // This is an actual attribute + _ => {} + } + } + if stage != CfgExprStage::EverythingElse { + warn!("Invalid cfg_attr attribute. {:?}", meta_tt); + return None; + } + Some(remove) +} +/// Removes a possible comma after the [AstNode] +fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet) { + if let Some(comma) = item.syntax().next_sibling_or_token().filter(|it| it.kind() == T![,]) { + res.insert(comma); + } +} +fn process_enum( + variants: VariantList, + loc: &MacroCallLoc, + db: &dyn ExpandDatabase, + remove: &mut FxHashSet, +) -> Option<()> { + 'variant: for variant in variants.variants() { + for attr in variant.attrs() { + if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { + // Rustc does not strip the attribute if it is enabled. So we will will leave it + debug!("censoring type {:?}", variant.syntax()); + remove.insert(variant.syntax().clone().into()); + // We need to remove the , as well + remove_possible_comma(&variant, remove); + continue 'variant; + }; + + if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if enabled { + debug!("Removing cfg_attr tokens {:?}", attr); + let meta = attr.meta()?; + let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; + remove.extend(removes_from_cfg_attr); + } else { + debug!("censoring type cfg_attr {:?}", variant.syntax()); + remove.insert(attr.syntax().clone().into()); + continue; + } + } + } + if let Some(fields) = variant.field_list() { + match fields { + ast::FieldList::RecordFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; + } + ast::FieldList::TupleFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; + } + } + } + } + Some(()) +} + +pub(crate) fn process_cfg_attrs( + node: &SyntaxNode, + loc: &MacroCallLoc, + db: &dyn ExpandDatabase, +) -> Option> { + // FIXME: #[cfg_eval] is not implemented. But it is not stable yet + if !matches!(loc.kind, MacroCallKind::Derive { .. }) { + return None; + } + let mut remove = FxHashSet::default(); + + let item = ast::Item::cast(node.clone())?; + for attr in item.attrs() { + if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if enabled { + debug!("Removing cfg_attr tokens {:?}", attr); + let meta = attr.meta()?; + let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; + remove.extend(removes_from_cfg_attr); + } else { + debug!("censoring type cfg_attr {:?}", item.syntax()); + remove.insert(attr.syntax().clone().into()); + continue; + } + } + } + match item { + ast::Item::Struct(it) => match it.field_list()? { + ast::FieldList::RecordFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; + } + ast::FieldList::TupleFieldList(fields) => { + process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; + } + }, + ast::Item::Enum(it) => { + process_enum(it.variant_list()?, loc, db, &mut remove)?; + } + ast::Item::Union(it) => { + process_has_attrs_with_possible_comma( + it.record_field_list()?.fields(), + loc, + db, + &mut remove, + )?; + } + // FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now + _ => {} + } + Some(remove) +} +/// Parses a `cfg` attribute from the meta +fn parse_from_attr_meta(meta: Meta) -> Option { + let tt = meta.token_tree()?; + let mut iter = tt.token_trees_and_tokens().skip(1).peekable(); + next_cfg_expr_from_syntax(&mut iter) +} + +fn next_cfg_expr_from_syntax(iter: &mut Peekable) -> Option +where + I: Iterator>, +{ + let name = match iter.next() { + None => return None, + Some(NodeOrToken::Token(element)) => match element.kind() { + syntax::T![ident] => SmolStr::new(element.text()), + _ => return Some(CfgExpr::Invalid), + }, + Some(_) => return Some(CfgExpr::Invalid), + }; + let result = match name.as_str() { + "all" | "any" | "not" => { + let mut preds = Vec::new(); + let Some(NodeOrToken::Node(tree)) = iter.next() else { + return Some(CfgExpr::Invalid); + }; + let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable(); + while tree_iter + .peek() + .filter( + |element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])), + ) + .is_some() + { + let pred = next_cfg_expr_from_syntax(&mut tree_iter); + if let Some(pred) = pred { + preds.push(pred); + } + } + let group = match name.as_str() { + "all" => CfgExpr::All(preds), + "any" => CfgExpr::Any(preds), + "not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))), + _ => unreachable!(), + }; + Some(group) + } + _ => match iter.peek() { + Some(NodeOrToken::Token(element)) if (element.kind() == syntax::T![=]) => { + iter.next(); + match iter.next() { + Some(NodeOrToken::Token(value_token)) + if (value_token.kind() == syntax::SyntaxKind::STRING) => + { + let value = value_token.text(); + let value = SmolStr::new(value.trim_matches('"')); + Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name, value })) + } + _ => None, + } + } + _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))), + }, + }; + if let Some(NodeOrToken::Token(element)) = iter.peek() { + if element.kind() == syntax::T![,] { + iter.next(); + } + } + result +} +#[cfg(test)] +mod tests { + use cfg::DnfExpr; + use expect_test::{expect, Expect}; + use syntax::{ast::Attr, AstNode, SourceFile}; + + use crate::cfg_process::parse_from_attr_meta; + + fn check_dnf_from_syntax(input: &str, expect: Expect) { + let parse = SourceFile::parse(input); + let node = match parse.tree().syntax().descendants().find_map(Attr::cast) { + Some(it) => it, + None => { + let node = std::any::type_name::(); + panic!("Failed to make ast node `{node}` from text {input}") + } + }; + let node = node.clone_subtree(); + assert_eq!(node.syntax().text_range().start(), 0.into()); + + let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap(); + let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); + expect.assert_eq(&actual); + } + #[test] + fn cfg_from_attr() { + check_dnf_from_syntax(r#"#[cfg(test)]"#, expect![[r#"#![cfg(test)]"#]]); + check_dnf_from_syntax(r#"#[cfg(not(never))]"#, expect![[r#"#![cfg(not(never))]"#]]); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs index 8b9e5a59df8..1a3dd0e7ddb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs @@ -48,7 +48,7 @@ impl ChangeWithProcMacros { } } - pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + pub fn change_file(&mut self, file_id: FileId, new_text: Option) { self.source_change.change_file(file_id, new_text) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 6f69ee15aca..ec68f2f96e5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -3,21 +3,19 @@ use base_db::{salsa, CrateId, FileId, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::{syntax_node_to_token_tree, ValueResult}; +use mbe::syntax_node_to_token_tree; use rustc_hash::FxHashSet; -use span::{AstIdMap, SyntaxContextData, SyntaxContextId}; -use syntax::{ - ast::{self, HasAttrs}, - AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, -}; +use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId}; +use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; use triomphe::Arc; use crate::{ - attrs::collect_attrs, + attrs::{collect_attrs, AttrId}, builtin_attr_macro::pseudo_derive_attr_expansion, builtin_fn_macro::EagerExpander, + cfg_process, declarative::DeclarativeMacroExpander, - fixup::{self, reverse_fixups, SyntaxFixupUndoInfo}, + fixup::{self, SyntaxFixupUndoInfo}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::ProcMacros, span_map::{RealSpanMap, SpanMap, SpanMapRef}, @@ -100,10 +98,7 @@ pub trait ExpandDatabase: SourceDatabase { /// Lowers syntactic macro call to a token tree representation. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. - fn macro_arg( - &self, - id: MacroCallId, - ) -> ValueResult<(Arc, SyntaxFixupUndoInfo), Arc>>; + fn macro_arg(&self, id: MacroCallId) -> (Arc, SyntaxFixupUndoInfo, Span); /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -120,6 +115,12 @@ pub trait ExpandDatabase: SourceDatabase { /// non-determinism breaks salsa in a very, very, very bad way. /// @edwin0cheng heroically debugged this once! See #4315 for details fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult>; + /// Retrieves the span to be used for a proc-macro expansions spans. + /// This is a firewall query as it requires parsing the file, which we don't want proc-macros to + /// directly depend on as that would cause to frequent invalidations, mainly because of the + /// parse queries being LRU cached. If they weren't the invalidations would only happen if the + /// user wrote in the file that defines the proc-macro. + fn proc_macro_span(&self, fun: AstId) -> Span; /// Firewall query that returns the errors from the `parse_macro_expansion` query. fn parse_macro_expansion_error( &self, @@ -139,30 +140,50 @@ pub fn expand_speculative( ) -> Option<(SyntaxNode, SyntaxToken)> { let loc = db.lookup_intern_macro_call(actual_macro_call); + // FIXME: This BOGUS here is dangerous once the proc-macro server can call back into the database! let span_map = RealSpanMap::absolute(FileId::BOGUS); let span_map = SpanMapRef::RealSpanMap(&span_map); + let (_, _, span) = db.macro_arg(actual_macro_call); + // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site), + mbe::syntax_node_to_token_tree(speculative_args, span_map, span), SyntaxFixupUndoInfo::NONE, ), - MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { - let censor = censor_for_macro_input(&loc, speculative_args); - let mut fixups = fixup::fixup_syntax(span_map, speculative_args, loc.call_site); + MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( + mbe::syntax_node_to_token_tree(speculative_args, span_map, span), + SyntaxFixupUndoInfo::NONE, + ), + MacroCallKind::Derive { derive_attr_index: index, .. } + | MacroCallKind::Attr { invoc_attr_index: index, .. } => { + let censor = if let MacroCallKind::Derive { .. } = loc.kind { + censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?) + } else { + attr_source(index, &ast::Item::cast(speculative_args.clone())?) + .into_iter() + .map(|it| it.syntax().clone().into()) + .collect() + }; + + let censor_cfg = + cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default(); + let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span); fixups.append.retain(|it, _| match it { - syntax::NodeOrToken::Node(it) => !censor.contains(it), syntax::NodeOrToken::Token(_) => true, + it => !censor.contains(it) && !censor_cfg.contains(it), }); fixups.remove.extend(censor); + fixups.remove.extend(censor_cfg); + ( mbe::syntax_node_to_token_tree_modified( speculative_args, span_map, fixups.append, fixups.remove, - loc.call_site, + span, ), fixups.undo_info, ) @@ -184,9 +205,8 @@ pub fn expand_speculative( }?; match attr.token_tree() { Some(token_tree) => { - let mut tree = - syntax_node_to_token_tree(token_tree.syntax(), span_map, loc.call_site); - tree.delimiter = tt::Delimiter::invisible_spanned(loc.call_site); + let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map, span); + tree.delimiter = tt::Delimiter::invisible_spanned(span); Some(tree) } @@ -199,36 +219,36 @@ pub fn expand_speculative( // Do the actual expansion, we need to directly expand the proc macro due to the attribute args // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. let mut speculative_expansion = match loc.def.kind { - MacroDefKind::ProcMacro(expander, ..) => { - tt.delimiter = tt::Delimiter::invisible_spanned(loc.call_site); + MacroDefKind::ProcMacro(expander, _, ast) => { + let span = db.proc_macro_span(ast); + tt.delimiter = tt::Delimiter::invisible_spanned(span); expander.expand( db, loc.def.krate, loc.krate, &tt, attr_arg.as_ref(), - span_with_def_site_ctxt(db, loc.def.span, actual_macro_call), - span_with_call_site_ctxt(db, loc.def.span, actual_macro_call), - span_with_mixed_site_ctxt(db, loc.def.span, actual_macro_call), + span_with_def_site_ctxt(db, span, actual_macro_call), + span_with_call_site_ctxt(db, span, actual_macro_call), + span_with_mixed_site_ctxt(db, span, actual_macro_call), ) } MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { - pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, loc.call_site) + pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) + } + MacroDefKind::Declarative(it) => { + db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span) + } + MacroDefKind::BuiltIn(it, _) => { + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } - MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand_unhygienic( - db, - tt, - loc.def.krate, - loc.call_site, - ), - MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into), MacroDefKind::BuiltInDerive(it, ..) => { - it.expand(db, actual_macro_call, &tt).map_err(Into::into) + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInEager(it, _) => { - it.expand(db, actual_macro_call, &tt).map_err(Into::into) + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } - MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt), + MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span), }; let expand_to = loc.expand_to(); @@ -319,181 +339,161 @@ pub(crate) fn parse_with_map( } } -// FIXME: for derive attributes, this will return separate copies of the same structures! +// FIXME: for derive attributes, this will return separate copies of the same structures! Though +// they may differ in spans due to differing call sites... fn macro_arg( db: &dyn ExpandDatabase, id: MacroCallId, - // FIXME: consider the following by putting fixup info into eager call info args - // ) -> ValueResult, Arc>> { -) -> ValueResult<(Arc, SyntaxFixupUndoInfo), Arc>> { +) -> (Arc, SyntaxFixupUndoInfo, Span) { let loc = db.lookup_intern_macro_call(id); - if let Some(EagerCallInfo { arg, .. }) = matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) - .then(|| loc.eager.as_deref()) - .flatten() + + if let MacroCallLoc { + def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. }, + kind: MacroCallKind::FnLike { eager: Some(eager), .. }, + .. + } = &loc { - ValueResult::ok((arg.clone(), SyntaxFixupUndoInfo::NONE)) - } else { - let (parse, map) = parse_with_map(db, loc.kind.file_id()); - let root = parse.syntax_node(); - - let syntax = match loc.kind { - MacroCallKind::FnLike { ast_id, .. } => { - let dummy_tt = |kind| { - ( - Arc::new(tt::Subtree { - delimiter: tt::Delimiter { - open: loc.call_site, - close: loc.call_site, - kind, - }, - token_trees: Box::default(), - }), - SyntaxFixupUndoInfo::default(), - ) - }; - - let node = &ast_id.to_ptr(db).to_node(&root); - let offset = node.syntax().text_range().start(); - let Some(tt) = node.token_tree() else { - return ValueResult::new( - dummy_tt(tt::DelimiterKind::Invisible), - Arc::new(Box::new([SyntaxError::new_at_offset( - "missing token tree".to_owned(), - offset, - )])), - ); - }; - let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']); - let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]); - - let mismatched_delimiters = !matches!( - (first, last), - (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) - ); - if mismatched_delimiters { - // Don't expand malformed (unbalanced) macro invocations. This is - // less than ideal, but trying to expand unbalanced macro calls - // sometimes produces pathological, deeply nested code which breaks - // all kinds of things. - // - // So instead, we'll return an empty subtree here - cov_mark::hit!(issue9358_bad_macro_stack_overflow); - - let kind = match first { - _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible, - T!['('] => tt::DelimiterKind::Parenthesis, - T!['['] => tt::DelimiterKind::Bracket, - T!['{'] => tt::DelimiterKind::Brace, - _ => tt::DelimiterKind::Invisible, - }; - return ValueResult::new( - dummy_tt(kind), - Arc::new(Box::new([SyntaxError::new_at_offset( - "mismatched delimiters".to_owned(), - offset, - )])), - ); - } - tt.syntax().clone() - } - MacroCallKind::Derive { ast_id, .. } => { - ast_id.to_ptr(db).to_node(&root).syntax().clone() - } - MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).to_node(&root).syntax().clone(), - }; - let (mut tt, undo_info) = match loc.kind { - MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree(&syntax, map.as_ref(), loc.call_site), - SyntaxFixupUndoInfo::NONE, - ), - MacroCallKind::Derive { .. } | MacroCallKind::Attr { .. } => { - let censor = censor_for_macro_input(&loc, &syntax); - let mut fixups = fixup::fixup_syntax(map.as_ref(), &syntax, loc.call_site); - fixups.append.retain(|it, _| match it { - syntax::NodeOrToken::Node(it) => !censor.contains(it), - syntax::NodeOrToken::Token(_) => true, - }); - fixups.remove.extend(censor); - { - let mut tt = mbe::syntax_node_to_token_tree_modified( - &syntax, - map.as_ref(), - fixups.append.clone(), - fixups.remove.clone(), - loc.call_site, - ); - reverse_fixups(&mut tt, &fixups.undo_info); - } - ( - mbe::syntax_node_to_token_tree_modified( - &syntax, - map, - fixups.append, - fixups.remove, - loc.call_site, - ), - fixups.undo_info, - ) - } - }; - - if loc.def.is_proc_macro() { - // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter.kind = tt::DelimiterKind::Invisible; - } - - if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) { - match parse.errors() { - errors if errors.is_empty() => ValueResult::ok((Arc::new(tt), undo_info)), - errors => ValueResult::new( - (Arc::new(tt), undo_info), - // Box::<[_]>::from(res.errors()), not stable yet - Arc::new(errors.to_vec().into_boxed_slice()), - ), - } - } else { - ValueResult::ok((Arc::new(tt), undo_info)) - } + return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span); } + + let (parse, map) = parse_with_map(db, loc.kind.file_id()); + let root = parse.syntax_node(); + + let (censor, item_node, span) = match loc.kind { + MacroCallKind::FnLike { ast_id, .. } => { + let node = &ast_id.to_ptr(db).to_node(&root); + let path_range = node + .path() + .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range()); + let span = map.span_for_range(path_range); + + let dummy_tt = |kind| { + ( + Arc::new(tt::Subtree { + delimiter: tt::Delimiter { open: span, close: span, kind }, + token_trees: Box::default(), + }), + SyntaxFixupUndoInfo::default(), + span, + ) + }; + + let Some(tt) = node.token_tree() else { + return dummy_tt(tt::DelimiterKind::Invisible); + }; + let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']); + let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]); + + let mismatched_delimiters = !matches!( + (first, last), + (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) + ); + if mismatched_delimiters { + // Don't expand malformed (unbalanced) macro invocations. This is + // less than ideal, but trying to expand unbalanced macro calls + // sometimes produces pathological, deeply nested code which breaks + // all kinds of things. + // + // So instead, we'll return an empty subtree here + cov_mark::hit!(issue9358_bad_macro_stack_overflow); + + let kind = match first { + _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible, + T!['('] => tt::DelimiterKind::Parenthesis, + T!['['] => tt::DelimiterKind::Bracket, + T!['{'] => tt::DelimiterKind::Brace, + _ => tt::DelimiterKind::Invisible, + }; + return dummy_tt(kind); + } + + let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), span); + if loc.def.is_proc_macro() { + // proc macros expect their inputs without parentheses, MBEs expect it with them included + tt.delimiter.kind = tt::DelimiterKind::Invisible; + } + return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); + } + MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { + let node = ast_id.to_ptr(db).to_node(&root); + let censor_derive_input = censor_derive_input(derive_attr_index, &node); + let item_node = node.into(); + let attr_source = attr_source(derive_attr_index, &item_node); + // FIXME: This is wrong, this should point to the path of the derive attribute` + let span = + map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else( + || item_node.syntax().text_range(), + |it| it.syntax().text_range(), + )); + (censor_derive_input, item_node, span) + } + MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + let node = ast_id.to_ptr(db).to_node(&root); + let attr_source = attr_source(invoc_attr_index, &node); + let span = map.span_for_range( + attr_source + .as_ref() + .and_then(|it| it.path()) + .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), + ); + (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + } + }; + + let (mut tt, undo_info) = { + let syntax = item_node.syntax(); + let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default(); + let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span); + fixups.append.retain(|it, _| match it { + syntax::NodeOrToken::Token(_) => true, + it => !censor.contains(it) && !censor_cfg.contains(it), + }); + fixups.remove.extend(censor); + fixups.remove.extend(censor_cfg); + + ( + mbe::syntax_node_to_token_tree_modified( + syntax, + map, + fixups.append, + fixups.remove, + span, + ), + fixups.undo_info, + ) + }; + + if loc.def.is_proc_macro() { + // proc macros expect their inputs without parentheses, MBEs expect it with them included + tt.delimiter.kind = tt::DelimiterKind::Invisible; + } + + (Arc::new(tt), undo_info, span) } // FIXME: Censoring info should be calculated by the caller! Namely by name resolution -/// Certain macro calls expect some nodes in the input to be preprocessed away, namely: -/// - derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped -/// - attributes expect the invoking attribute to be stripped -fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet { +/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped +fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet { // FIXME: handle `cfg_attr` - (|| { - let censor = match loc.kind { - MacroCallKind::FnLike { .. } => return None, - MacroCallKind::Derive { derive_attr_index, .. } => { - cov_mark::hit!(derive_censoring); - ast::Item::cast(node.clone())? - .attrs() - .take(derive_attr_index.ast_index() + 1) - // FIXME, this resolution should not be done syntactically - // derive is a proper macro now, no longer builtin - // But we do not have resolution at this stage, this means - // we need to know about all macro calls for the given ast item here - // so we require some kind of mapping... - .filter(|attr| attr.simple_name().as_deref() == Some("derive")) - .map(|it| it.syntax().clone()) - .collect() - } - MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None, - MacroCallKind::Attr { invoc_attr_index, .. } => { - cov_mark::hit!(attribute_macro_attr_censoring); - collect_attrs(&ast::Item::cast(node.clone())?) - .nth(invoc_attr_index.ast_index()) - .and_then(|x| Either::left(x.1)) - .map(|attr| attr.syntax().clone()) - .into_iter() - .collect() - } - }; - Some(censor) - })() - .unwrap_or_default() + cov_mark::hit!(derive_censoring); + collect_attrs(node) + .take(derive_attr_index.ast_index() + 1) + .filter_map(|(_, attr)| Either::left(attr)) + // FIXME, this resolution should not be done syntactically + // derive is a proper macro now, no longer builtin + // But we do not have resolution at this stage, this means + // we need to know about all macro calls for the given ast item here + // so we require some kind of mapping... + .filter(|attr| attr.simple_name().as_deref() == Some("derive")) + .map(|it| it.syntax().clone().into()) + .collect() +} + +/// Attributes expect the invoking attribute to be stripped +fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option { + // FIXME: handle `cfg_attr` + cov_mark::hit!(attribute_macro_attr_censoring); + collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr)) } impl TokenExpander { @@ -523,74 +523,64 @@ fn macro_expand( ) -> ExpandResult> { let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered(); - let ExpandResult { value: tt, mut err } = match loc.def.kind { + let (ExpandResult { value: tt, err }, span) = match loc.def.kind { MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), _ => { - let ValueResult { value: (macro_arg, undo_info), err } = db.macro_arg(macro_call_id); - let format_parse_err = |err: Arc>| { - let mut buf = String::new(); - for err in &**err { - use std::fmt::Write; - _ = write!(buf, "{}, ", err); - } - buf.pop(); - buf.pop(); - ExpandError::other(buf) - }; + let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id); let arg = &*macro_arg; - let res = match loc.def.kind { - MacroDefKind::Declarative(id) => { - db.decl_macro_expander(loc.def.krate, id).expand(db, arg.clone(), macro_call_id) - } - MacroDefKind::BuiltIn(it, _) => { - it.expand(db, macro_call_id, arg).map_err(Into::into) - } - // This might look a bit odd, but we do not expand the inputs to eager macros here. - // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. - // That kind of expansion uses the ast id map of an eager macros input though which goes through - // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query - // will end up going through here again, whereas we want to just want to inspect the raw input. - // As such we just return the input subtree here. - MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => { - return ExpandResult { - value: CowArc::Arc(macro_arg.clone()), - err: err.map(format_parse_err), - }; - } - MacroDefKind::BuiltInDerive(it, _) => { - it.expand(db, macro_call_id, arg).map_err(Into::into) - } - MacroDefKind::BuiltInEager(it, _) => { - it.expand(db, macro_call_id, arg).map_err(Into::into) - } - MacroDefKind::BuiltInAttr(it, _) => { - let mut res = it.expand(db, macro_call_id, arg); - fixup::reverse_fixups(&mut res.value, &undo_info); - res - } - _ => unreachable!(), - }; - ExpandResult { - value: res.value, - // if the arg had parse errors, show them instead of the expansion errors - err: err.map(format_parse_err).or(res.err), - } + let res = + match loc.def.kind { + MacroDefKind::Declarative(id) => db + .decl_macro_expander(loc.def.krate, id) + .expand(db, arg.clone(), macro_call_id, span), + MacroDefKind::BuiltIn(it, _) => { + it.expand(db, macro_call_id, arg, span).map_err(Into::into) + } + MacroDefKind::BuiltInDerive(it, _) => { + it.expand(db, macro_call_id, arg, span).map_err(Into::into) + } + MacroDefKind::BuiltInEager(it, _) => { + // This might look a bit odd, but we do not expand the inputs to eager macros here. + // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. + // That kind of expansion uses the ast id map of an eager macros input though which goes through + // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query + // will end up going through here again, whereas we want to just want to inspect the raw input. + // As such we just return the input subtree here. + let eager = match &loc.kind { + MacroCallKind::FnLike { eager: None, .. } => { + return ExpandResult::ok(CowArc::Arc(macro_arg.clone())); + } + MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), + _ => None, + }; + + let mut res = it.expand(db, macro_call_id, arg, span).map_err(Into::into); + + if let Some(EagerCallInfo { error, .. }) = eager { + // FIXME: We should report both errors! + res.err = error.clone().or(res.err); + } + res + } + MacroDefKind::BuiltInAttr(it, _) => { + let mut res = it.expand(db, macro_call_id, arg, span); + fixup::reverse_fixups(&mut res.value, &undo_info); + res + } + _ => unreachable!(), + }; + (ExpandResult { value: res.value, err: res.err }, span) } }; - if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() { - // FIXME: We should report both errors! - err = error.clone().or(err); - } - // Skip checking token tree limit for include! macro call if !loc.def.is_include() { // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value.map(|()| { CowArc::Owned(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(loc.call_site), + delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([]), }) }); @@ -600,12 +590,23 @@ fn macro_expand( ExpandResult { value: CowArc::Owned(tt), err } } +fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { + let root = db.parse_or_expand(ast.file_id); + let ast_id_map = &db.ast_id_map(ast.file_id); + let span_map = &db.span_map(ast.file_id); + + let node = ast_id_map.get(ast.value).to_node(&root); + let range = ast::HasName::name(&node) + .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range()); + span_map.span_for_range(range) +} + fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let loc = db.lookup_intern_macro_call(id); - let (macro_arg, undo_info) = db.macro_arg(id).value; + let (macro_arg, undo_info, span) = db.macro_arg(id); - let expander = match loc.def.kind { - MacroDefKind::ProcMacro(expander, ..) => expander, + let (expander, ast) = match loc.def.kind { + MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast), _ => unreachable!(), }; @@ -614,22 +615,25 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult None, }; - let ExpandResult { value: mut tt, err } = expander.expand( - db, - loc.def.krate, - loc.krate, - ¯o_arg, - attr_arg, - span_with_def_site_ctxt(db, loc.def.span, id), - span_with_call_site_ctxt(db, loc.def.span, id), - span_with_mixed_site_ctxt(db, loc.def.span, id), - ); + let ExpandResult { value: mut tt, err } = { + let span = db.proc_macro_span(ast); + expander.expand( + db, + loc.def.krate, + loc.krate, + ¯o_arg, + attr_arg, + span_with_def_site_ctxt(db, span, id), + span_with_call_site_ctxt(db, span, id), + span_with_mixed_site_ctxt(db, span, id), + ) + }; // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value.map(|()| { Arc::new(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(loc.call_site), + delimiter: tt::Delimiter::invisible_spanned(span), token_trees: Box::new([]), }) }); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 6874336cd2d..33643c02724 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -29,6 +29,7 @@ impl DeclarativeMacroExpander { db: &dyn ExpandDatabase, tt: tt::Subtree, call_id: MacroCallId, + span: Span, ) -> ExpandResult { let loc = db.lookup_intern_macro_call(call_id); let toolchain = db.toolchain(loc.def.krate); @@ -45,7 +46,7 @@ impl DeclarativeMacroExpander { }); match self.mac.err() { Some(_) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }), + tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), ExpandError::MacroDefinition, ), None => self @@ -54,7 +55,7 @@ impl DeclarativeMacroExpander { &tt, |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), new_meta_vars, - loc.call_site, + span, ) .map_err(Into::into), } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 5337a5bb028..8b147c88c13 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -19,7 +19,7 @@ //! //! See the full discussion : use base_db::CrateId; -use span::Span; +use span::SyntaxContextId; use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; use triomphe::Arc; @@ -27,22 +27,20 @@ use crate::{ ast::{self, AstNode}, db::ExpandDatabase, mod_path::ModPath, - EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern, + AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, }; pub fn expand_eager_macro_input( db: &dyn ExpandDatabase, krate: CrateId, - macro_call: InFile, + macro_call: &ast::MacroCall, + ast_id: AstId, def: MacroDefId, - call_site: Span, + call_site: SyntaxContextId, resolver: &dyn Fn(ModPath) -> Option, ) -> ExpandResult> { - let ast_map = db.ast_id_map(macro_call.file_id); - // the expansion which the ast id map is built upon has no whitespace, so the offsets are wrong as macro_call is from the token tree that has whitespace! - let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); - let expand_to = ExpandTo::from_call_site(¯o_call.value); + let expand_to = ExpandTo::from_call_site(macro_call); // Note: // When `lazy_expand` is called, its *parent* file must already exist. @@ -51,11 +49,11 @@ pub fn expand_eager_macro_input( let arg_id = MacroCallLoc { def, krate, - eager: None, - kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, - call_site, + kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None }, + ctxt: call_site, } .intern(db); + let (_, _, span) = db.macro_arg(arg_id); let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = db.parse_macro_expansion(arg_id.as_macro_file()); @@ -82,16 +80,24 @@ pub fn expand_eager_macro_input( return ExpandResult { value: None, err }; }; - let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, call_site); + let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, span); subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; let loc = MacroCallLoc { def, krate, - eager: Some(Arc::new(EagerCallInfo { arg: Arc::new(subtree), arg_id, error: err.clone() })), - kind: MacroCallKind::FnLike { ast_id: call_id, expand_to }, - call_site, + kind: MacroCallKind::FnLike { + ast_id, + expand_to, + eager: Some(Arc::new(EagerCallInfo { + arg: Arc::new(subtree), + arg_id, + error: err.clone(), + span, + })), + }, + ctxt: call_site, }; ExpandResult { value: Some(loc.intern(db)), err } @@ -100,15 +106,18 @@ pub fn expand_eager_macro_input( fn lazy_expand( db: &dyn ExpandDatabase, def: &MacroDefId, - macro_call: InFile, + macro_call: &ast::MacroCall, + ast_id: AstId, krate: CrateId, - call_site: Span, + call_site: SyntaxContextId, ) -> ExpandResult<(InFile>, Arc)> { - let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); - - let expand_to = ExpandTo::from_call_site(¯o_call.value); - let ast_id = macro_call.with_value(ast_id); - let id = def.as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id, expand_to }, call_site); + let expand_to = ExpandTo::from_call_site(macro_call); + let id = def.make_call( + db, + krate, + MacroCallKind::FnLike { ast_id, expand_to, eager: None }, + call_site, + ); let macro_file = id.as_macro_file(); db.parse_macro_expansion(macro_file) @@ -122,7 +131,7 @@ fn eager_macro_recur( mut offset: TextSize, curr: InFile, krate: CrateId, - call_site: Span, + call_site: SyntaxContextId, macro_resolver: &dyn Fn(ModPath) -> Option, ) -> ExpandResult> { let original = curr.value.clone_for_update(); @@ -172,12 +181,14 @@ fn eager_macro_recur( continue; } }; + let ast_id = db.ast_id_map(curr.file_id).ast_id(&call); let ExpandResult { value, err } = match def.kind { MacroDefKind::BuiltInEager(..) => { let ExpandResult { value, err } = expand_eager_macro_input( db, krate, - curr.with_value(call.clone()), + &call, + curr.with_value(ast_id), def, call_site, macro_resolver, @@ -207,7 +218,7 @@ fn eager_macro_recur( | MacroDefKind::BuiltInDerive(..) | MacroDefKind::ProcMacro(..) => { let ExpandResult { value: (parse, tm), err } = - lazy_expand(db, &def, curr.with_value(call.clone()), krate, call_site); + lazy_expand(db, &def, &call, curr.with_value(ast_id), krate, call_site); // replace macro inside let ExpandResult { value, err: error } = eager_macro_recur( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index a500c24ce88..04a4851ddb7 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -10,7 +10,7 @@ use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, use crate::{ db::{self, ExpandDatabase}, - map_node_range_up, span_for_offset, MacroFileIdExt, + map_node_range_up, map_node_range_up_rooted, span_for_offset, MacroFileIdExt, }; /// `InFile` stores a value of `T` inside a particular file/syntax tree. @@ -38,6 +38,9 @@ impl AstId { pub fn to_node(&self, db: &dyn ExpandDatabase) -> N { self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) } + pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { + self.to_ptr(db).text_range() + } pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile { crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))) } @@ -49,6 +52,9 @@ impl AstId { pub type ErasedAstId = crate::InFile; impl ErasedAstId { + pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange { + self.to_ptr(db).text_range() + } pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr { db.ast_id_map(self.file_id).get_erased(self.value) } @@ -173,24 +179,8 @@ impl InFile<&SyntaxNode> { /// /// For attributes and derives, this will point back to the attribute only. /// For the entire item use [`InFile::original_file_range_full`]. - pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange { - match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, - HirFileIdRepr::MacroFile(mac_file) => { - if let Some((res, ctxt)) = - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range()) - { - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behaviour. - if ctxt.is_root() { - return res; - } - } - // Fall back to whole macro call. - let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); - loc.kind.original_call_range(db) - } - } + pub fn original_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange { + self.map(SyntaxNode::text_range).original_node_file_range_rooted(db) } /// Falls back to the macro call range if the node cannot be mapped up fully. @@ -198,23 +188,7 @@ impl InFile<&SyntaxNode> { self, db: &dyn db::ExpandDatabase, ) -> FileRange { - match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() }, - HirFileIdRepr::MacroFile(mac_file) => { - if let Some((res, ctxt)) = - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range()) - { - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behaviour. - if ctxt.is_root() { - return res; - } - } - // Fall back to whole macro call. - let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); - loc.kind.original_call_range_with_body(db) - } - } + self.map(SyntaxNode::text_range).original_node_file_range_with_macro_call_body(db) } /// Attempts to map the syntax node back up its macro calls. @@ -222,17 +196,10 @@ impl InFile<&SyntaxNode> { self, db: &dyn db::ExpandDatabase, ) -> Option<(FileRange, SyntaxContextId)> { - match self.file_id.repr() { - HirFileIdRepr::FileId(file_id) => { - Some((FileRange { file_id, range: self.value.text_range() }, SyntaxContextId::ROOT)) - } - HirFileIdRepr::MacroFile(mac_file) => { - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range()) - } - } + self.map(SyntaxNode::text_range).original_node_file_range_opt(db) } - pub fn original_syntax_node( + pub fn original_syntax_node_rooted( self, db: &dyn db::ExpandDatabase, ) -> Option> { @@ -242,25 +209,21 @@ impl InFile<&SyntaxNode> { HirFileIdRepr::FileId(file_id) => { return Some(InRealFile { file_id, value: self.value.clone() }) } - HirFileIdRepr::MacroFile(m) => m, + HirFileIdRepr::MacroFile(m) if m.is_attr_macro(db) => m, + _ => return None, }; - if !file_id.is_attr_macro(db) { - return None; - } - let (FileRange { file_id, range }, ctx) = - map_node_range_up(db, &db.expansion_span_map(file_id), self.value.text_range())?; + let FileRange { file_id, range } = + map_node_range_up_rooted(db, &db.expansion_span_map(file_id), self.value.text_range())?; - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behavior. - if !ctx.is_root() { - return None; - } - - let anc = db.parse(file_id).syntax_node().covering_element(range); let kind = self.value.kind(); - // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes? - let value = anc.ancestors().find(|it| it.kind() == kind)?; + let value = db + .parse(file_id) + .syntax_node() + .covering_element(range) + .ancestors() + .take_while(|it| it.text_range() == range) + .find(|it| it.kind() == kind)?; Some(InRealFile::new(file_id, value)) } } @@ -355,8 +318,8 @@ impl InFile { match self.file_id.repr() { HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileIdRepr::MacroFile(mac_file) => { - match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) { - Some((it, SyntaxContextId::ROOT)) => it, + match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) { + Some(it) => it, _ => { let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); loc.kind.original_call_range(db) @@ -366,6 +329,24 @@ impl InFile { } } + pub fn original_node_file_range_with_macro_call_body( + self, + db: &dyn db::ExpandDatabase, + ) -> FileRange { + match self.file_id.repr() { + HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value }, + HirFileIdRepr::MacroFile(mac_file) => { + match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) { + Some(it) => it, + _ => { + let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); + loc.kind.original_call_range_with_body(db) + } + } + } + } + } + pub fn original_node_file_range_opt( self, db: &dyn db::ExpandDatabase, @@ -395,18 +376,12 @@ impl InFile { return None; } - let (FileRange { file_id, range }, ctx) = map_node_range_up( + let FileRange { file_id, range } = map_node_range_up_rooted( db, &db.expansion_span_map(file_id), self.value.syntax().text_range(), )?; - // FIXME: Figure out an API that makes proper use of ctx, this only exists to - // keep pre-token map rewrite behaviour. - if !ctx.is_root() { - return None; - } - // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes? let anc = db.parse(file_id).syntax_node().covering_element(range); let value = anc.ancestors().find_map(N::cast)?; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index a3b2c700ffe..959595afb57 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -3,7 +3,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; -use span::{ErasedFileAstId, Span, SpanAnchor, SpanData, FIXUP_ERASED_FILE_AST_ID_MARKER}; +use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER}; use stdx::never; use syntax::{ ast::{self, AstNode, HasLoopBody}, @@ -23,7 +23,7 @@ use crate::{ #[derive(Debug, Default)] pub(crate) struct SyntaxFixups { pub(crate) append: FxHashMap>, - pub(crate) remove: FxHashSet, + pub(crate) remove: FxHashSet, pub(crate) undo_info: SyntaxFixupUndoInfo, } @@ -51,13 +51,13 @@ pub(crate) fn fixup_syntax( call_site: Span, ) -> SyntaxFixups { let mut append = FxHashMap::::default(); - let mut remove = FxHashSet::::default(); + let mut remove = FxHashSet::::default(); let mut preorder = node.preorder(); let mut original = Vec::new(); let dummy_range = FIXUP_DUMMY_RANGE; let fake_span = |range| { let span = span_map.span_for_range(range); - SpanData { + Span { range: dummy_range, anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, ctx: span.ctx, @@ -68,7 +68,7 @@ pub(crate) fn fixup_syntax( let node_range = node.text_range(); if can_handle_error(&node) && has_error_to_handle(&node) { - remove.insert(node.clone()); + remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site); let idx = original.len() as u32; @@ -76,7 +76,7 @@ pub(crate) fn fixup_syntax( let span = span_map.span_for_range(node_range); let replacement = Leaf::Ident(Ident { text: "__ra_fixup".into(), - span: SpanData { + span: Span { range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END), anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor }, ctx: span.ctx, @@ -305,8 +305,8 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID ) { - tt.delimiter.close = SpanData::DUMMY; - tt.delimiter.open = SpanData::DUMMY; + tt.delimiter.close = Span::DUMMY; + tt.delimiter.open = Span::DUMMY; } reverse_fixups_(tt, undo_info); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index ac2bab280d5..097e760c70a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -65,7 +65,7 @@ pub(super) fn apply_mark( return apply_mark_internal(db, ctxt, call_id, transparency); } - let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site.ctx; + let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt; let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { call_site_ctxt.normalize_to_macros_2_0(db) } else { @@ -205,11 +205,10 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String { let id = e.key; let expn_data = e.value.as_ref().unwrap(); s.push_str(&format!( - "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}", + "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, kind: {:?}", id, expn_data.kind.file_id(), - expn_data.call_site, - SyntaxContextId::ROOT, // FIXME expn_data.def_site, + expn_data.ctxt, expn_data.kind.descr(), )); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 42dc8c12d60..5d4f7dc1462 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -22,16 +22,19 @@ pub mod proc_macro; pub mod quote; pub mod span_map; +mod cfg_process; mod fixup; - use attrs::collect_attrs; +use rustc_hash::FxHashMap; use triomphe::Arc; use std::{fmt, hash::Hash}; use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId}; use either::Either; -use span::{ErasedFileAstId, FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId}; +use span::{ + ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, +}; use syntax::{ ast::{self, AstNode}, SyntaxNode, SyntaxToken, TextRange, TextSize, @@ -167,13 +170,8 @@ impl fmt::Display for ExpandError { pub struct MacroCallLoc { pub def: MacroDefId, pub krate: CrateId, - /// Some if this is a macro call for an eager macro. Note that this is `None` - /// for the eager input macro file. - // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing - // leakage problems here - eager: Option>, pub kind: MacroCallKind, - pub call_site: Span, + pub ctxt: SyntaxContextId, } impl_intern_value_trivial!(MacroCallLoc); @@ -184,7 +182,6 @@ pub struct MacroDefId { pub kind: MacroDefKind, pub local_inner: bool, pub allow_internal_unsafe: bool, - pub span: Span, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -204,6 +201,8 @@ pub struct EagerCallInfo { /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, + /// TODO: Doc + span: Span, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -211,6 +210,11 @@ pub enum MacroCallKind { FnLike { ast_id: AstId, expand_to: ExpandTo, + /// Some if this is a macro call for an eager macro. Note that this is `None` + /// for the eager input macro file. + // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing + // leakage problems here + eager: Option>, }, Derive { ast_id: AstId, @@ -272,7 +276,7 @@ impl HirFileIdExt for HirFileId { HirFileIdRepr::MacroFile(file) => { let loc = db.lookup_intern_macro_call(file.macro_call_id); if loc.def.is_include() { - if let Some(eager) = &loc.eager { + if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind { if let Ok(it) = builtin_fn_macro::include_input_to_file_id( db, file.macro_call_id, @@ -319,6 +323,9 @@ impl HirFileIdExt for HirFileId { } pub trait MacroFileIdExt { + fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool; + fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool; + fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option; fn expansion_level(self, db: &dyn ExpandDatabase) -> u32; /// If this is a macro call, returns the syntax node of the call. fn call_node(self, db: &dyn ExpandDatabase) -> InFile; @@ -385,31 +392,47 @@ impl MacroFileIdExt for MacroFileId { db.lookup_intern_macro_call(self.macro_call_id).def.is_include() } + fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool { + db.lookup_intern_macro_call(self.macro_call_id).def.is_include_like() + } + + fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool { + db.lookup_intern_macro_call(self.macro_call_id).def.is_env_or_option_env() + } + fn is_eager(&self, db: &dyn ExpandDatabase) -> bool { - let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + let loc = db.lookup_intern_macro_call(self.macro_call_id); matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) } + fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option { + let loc = db.lookup_intern_macro_call(self.macro_call_id); + match &loc.kind { + MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id), + _ => None, + } + } + fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool { - let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + let loc = db.lookup_intern_macro_call(self.macro_call_id); matches!(loc.kind, MacroCallKind::Attr { .. }) } fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool { - let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id); + let loc = db.lookup_intern_macro_call(self.macro_call_id); loc.def.is_attribute_derive() } } impl MacroDefId { - pub fn as_lazy_macro( + pub fn make_call( self, db: &dyn ExpandDatabase, krate: CrateId, kind: MacroCallKind, - call_site: Span, + ctxt: SyntaxContextId, ) -> MacroCallId { - MacroCallLoc { def: self, krate, eager: None, kind, call_site }.intern(db) + MacroCallLoc { def: self, krate, kind, ctxt }.intern(db) } pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile { @@ -474,6 +497,14 @@ impl MacroDefId { pub fn is_include(&self) -> bool { matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include()) } + + pub fn is_include_like(&self) -> bool { + matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include_like()) + } + + pub fn is_env_or_option_env(&self) -> bool { + matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_env_or_option_env()) + } } impl MacroCallLoc { @@ -531,7 +562,7 @@ impl MacroCallLoc { macro_call_id: MacroCallId, ) -> Option { if self.def.is_include() { - if let Some(eager) = &self.eager { + if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind { if let Ok(it) = builtin_fn_macro::include_input_to_file_id(db, macro_call_id, &eager.arg) { @@ -655,7 +686,7 @@ impl MacroCallKind { /// ExpansionInfo mainly describes how to map text range between src and expanded macro // FIXME: can be expensive to create, we should check the use sites and maybe replace them with // simpler function calls if the map is only used once -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ExpansionInfo { pub expanded: InMacroFile, /// The argument TokenTree or item for attributes @@ -683,6 +714,24 @@ impl ExpansionInfo { } /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. + /// + /// Note this does a linear search through the entire backing vector of the spanmap. + pub fn map_range_down_exact( + &self, + span: Span, + ) -> Option + '_>> { + let tokens = self + .exp_map + .ranges_with_span_exact(span) + .flat_map(move |range| self.expanded.value.covering_element(range).into_token()); + + Some(InMacroFile::new(self.expanded.file_id, tokens)) + } + + /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. + /// Unlike [`map_range_down_exact`], this will consider spans that contain the given span. + /// + /// Note this does a linear search through the entire backing vector of the spanmap. pub fn map_range_down( &self, span: Span, @@ -739,7 +788,7 @@ impl ExpansionInfo { InFile::new( self.arg.file_id, arg_map - .ranges_with_span(span) + .ranges_with_span_exact(span) .filter(|range| range.intersect(arg_range).is_some()) .collect(), ) @@ -757,7 +806,7 @@ impl ExpansionInfo { let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; - let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value; + let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id); let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { @@ -793,7 +842,34 @@ impl ExpansionInfo { } } +/// Maps up the text range out of the expansion hierarchy back into the original file its from only +/// considering the root spans contained. +/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ. +pub fn map_node_range_up_rooted( + db: &dyn ExpandDatabase, + exp_map: &ExpansionSpanMap, + range: TextRange, +) -> Option { + let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root()); + let Span { range, anchor, ctx: _ } = spans.next()?; + let mut start = range.start(); + let mut end = range.end(); + + for span in spans { + if span.anchor != anchor { + return None; + } + start = start.min(span.range.start()); + end = end.max(span.range.end()); + } + let anchor_offset = + db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start(); + Some(FileRange { file_id: anchor.file_id, range: TextRange::new(start, end) + anchor_offset }) +} + /// Maps up the text range out of the expansion hierarchy back into the original file its from. +/// +/// this will return `None` if any anchors or syntax contexts differ. pub fn map_node_range_up( db: &dyn ExpandDatabase, exp_map: &ExpansionSpanMap, @@ -819,6 +895,29 @@ pub fn map_node_range_up( )) } +/// Maps up the text range out of the expansion hierarchy back into the original file its from. +/// This version will aggregate the ranges of all spans with the same anchor and syntax context. +pub fn map_node_range_up_aggregated( + db: &dyn ExpandDatabase, + exp_map: &ExpansionSpanMap, + range: TextRange, +) -> FxHashMap<(SpanAnchor, SyntaxContextId), TextRange> { + let mut map = FxHashMap::default(); + for span in exp_map.spans_for_range(range) { + let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range); + *range = TextRange::new( + range.start().min(span.range.start()), + range.end().max(span.range.end()), + ); + } + for ((anchor, _), range) in &mut map { + let anchor_offset = + db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start(); + *range += anchor_offset; + } + map +} + /// Looks up the span at the given offset. pub fn span_for_offset( db: &dyn ExpandDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs index c1930c94f5c..a31a111c911 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs @@ -266,10 +266,11 @@ mod tests { let quoted = quote!(DUMMY =>#a); assert_eq!(quoted.to_string(), "hello"); - let t = format!("{quoted:?}"); + let t = format!("{quoted:#?}"); expect![[r#" - SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } - IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t); + SUBTREE $$ 937550:0@0..0#0 937550:0@0..0#0 + IDENT hello 937550:0@0..0#0"#]] + .assert_eq(&t); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index ef86be67096..eae2c8fb632 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs @@ -1,13 +1,15 @@ //! Span maps for real files and macro expansions. -use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span}; -use syntax::{AstNode, TextRange}; + +use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId}; +use stdx::TupleExt; +use syntax::{ast, AstNode, TextRange}; use triomphe::Arc; pub use span::RealSpanMap; -use crate::db::ExpandDatabase; +use crate::{attrs::collect_attrs, db::ExpandDatabase}; -pub type ExpansionSpanMap = span::SpanMap; +pub type ExpansionSpanMap = span::SpanMap; /// Spanmap for a macro file or a real file #[derive(Clone, Debug, PartialEq, Eq)] @@ -82,13 +84,54 @@ pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc + { + if let Some(extern_item_list) = it.extern_item_list() { + pairs.extend( + extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry), + ); + } + } + ast::Item::Impl(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + if let Some(assoc_item_list) = it.assoc_item_list() { + pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry)); + } + } + ast::Item::Module(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + if let Some(item_list) = it.item_list() { + pairs.extend(item_list.items().map(item_to_entry)); + } + } + ast::Item::Trait(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => { + if let Some(assoc_item_list) = it.assoc_item_list() { + pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry)); + } + } + _ => (), + }); Arc::new(RealSpanMap::from_file( file_id, diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 41e2f7ad73c..3cfedcdcb4d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -23,10 +23,10 @@ oorandom = "11.1.3" tracing.workspace = true rustc-hash.workspace = true scoped-tls = "1.0.0" -chalk-solve = { version = "0.96.0", default-features = false } -chalk-ir = "0.96.0" -chalk-recursive = { version = "0.96.0", default-features = false } -chalk-derive = "0.96.0" +chalk-solve.workspace = true +chalk-ir.workspace = true +chalk-recursive.workspace = true +chalk-derive.workspace = true la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 8d819e41aa2..e2446c34254 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -113,7 +113,7 @@ pub(crate) fn autoderef_step( ty: Ty, explicit: bool, ) -> Option<(AutoderefKind, Ty)> { - if let Some(derefed) = builtin_deref(table, &ty, explicit) { + if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) @@ -121,7 +121,7 @@ pub(crate) fn autoderef_step( } pub(crate) fn builtin_deref<'ty>( - table: &mut InferenceTable<'_>, + db: &dyn HirDatabase, ty: &'ty Ty, explicit: bool, ) -> Option<&'ty Ty> { @@ -129,7 +129,7 @@ pub(crate) fn builtin_deref<'ty>( TyKind::Ref(.., ty) => Some(ty), TyKind::Raw(.., ty) if explicit => Some(ty), &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => { - if crate::lang_items::is_box(table.db, adt) { + if crate::lang_items::is_box(db, adt) { substs.at(Interner, 0).ty(Interner) } else { None diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 9cea414e1a0..34ba17f145e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -22,7 +22,7 @@ mod pat; mod path; pub(crate) mod unify; -use std::{convert::identity, ops::Index}; +use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, @@ -777,7 +777,15 @@ impl<'a> InferenceContext<'a> { param_tys.push(va_list_ty) } - for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) { + let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var())); + if let Some(self_param) = self.body.self_param { + if let Some(ty) = param_tys.next() { + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + self.write_binding_ty(self_param, ty); + } + } + for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 61638c43d9c..ff6de61ba64 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -647,7 +647,7 @@ impl InferenceTable<'_> { let goal: InEnvironment = InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner)); - let canonicalized = self.canonicalize(goal); + let canonicalized = self.canonicalize_with_free_vars(goal); // FIXME: rustc's coerce_unsized is more specialized -- it only tries to // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index c377a51e7d3..a3dab1fd9d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -312,15 +312,13 @@ impl InferenceContext<'_> { Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); - let (res, derefed_callee) = 'b: { - // manual loop to be able to access `derefs.table` - while let Some((callee_deref_ty, _)) = derefs.next() { - let res = derefs.table.callable_sig(&callee_deref_ty, args.len()); - if res.is_some() { - break 'b (res, callee_deref_ty); - } + let (res, derefed_callee) = loop { + let Some((callee_deref_ty, _)) = derefs.next() else { + break (None, callee_ty.clone()); + }; + if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) { + break (Some(res), callee_deref_ty); } - (None, callee_ty.clone()) }; // if the function is unresolved, we use is_varargs=true to // suppress the arg count diagnostic here @@ -657,7 +655,7 @@ impl InferenceContext<'_> { ); } } - if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) { + if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) } else { deref_by_trait(&mut self.table, inner_ty) @@ -774,7 +772,7 @@ impl InferenceContext<'_> { let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, self.table.trait_env.clone(), - canonicalized.value, + canonicalized, index_trait, ); let (self_ty, mut adj) = receiver_adjustments @@ -1559,7 +1557,7 @@ impl InferenceContext<'_> { let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); let resolved = method_resolution::lookup_method( self.db, - &canonicalized_receiver.value, + &canonicalized_receiver, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), VisibleFromModule::Filter(self.resolver.module()), @@ -1608,7 +1606,7 @@ impl InferenceContext<'_> { let resolved = method_resolution::lookup_method( self.db, - &canonicalized_receiver.value, + &canonicalized_receiver, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), VisibleFromModule::Filter(self.resolver.module()), @@ -1641,7 +1639,7 @@ impl InferenceContext<'_> { }; let assoc_func_with_same_name = method_resolution::iterate_method_candidates( - &canonicalized_receiver.value, + &canonicalized_receiver, self.db, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 16ae028427d..8f537bb448b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -321,7 +321,7 @@ impl InferenceContext<'_> { let mut not_visible = None; let res = method_resolution::iterate_method_candidates( - &canonical_ty.value, + &canonical_ty, self.db, self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 1d0150d850f..be7547f9bae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -23,12 +23,9 @@ use crate::{ }; impl InferenceContext<'_> { - pub(super) fn canonicalize + HasInterner>( - &mut self, - t: T, - ) -> Canonicalized + pub(super) fn canonicalize(&mut self, t: T) -> Canonical where - T: HasInterner, + T: TypeFoldable + HasInterner, { self.table.canonicalize(t) } @@ -128,14 +125,14 @@ impl> Canonicalized { }), ); for (i, v) in solution.value.iter(Interner).enumerate() { - let var = self.free_vars[i].clone(); + let var = &self.free_vars[i]; if let Some(ty) = v.ty(Interner) { // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); ctx.unify(var.assert_ty_ref(Interner), &ty); } else { - let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), Interner)); + let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner)); } } } @@ -243,7 +240,7 @@ pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, var_unification_table: ChalkInferenceTable, - type_variable_table: Vec, + type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on /// temporary allocations. @@ -252,8 +249,8 @@ pub(crate) struct InferenceTable<'a> { pub(crate) struct InferenceTableSnapshot { var_table_snapshot: chalk_solve::infer::InferenceSnapshot, + type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, - type_variable_table_snapshot: Vec, } impl<'a> InferenceTable<'a> { @@ -262,7 +259,7 @@ impl<'a> InferenceTable<'a> { db, trait_env, var_unification_table: ChalkInferenceTable::new(), - type_variable_table: Vec::new(), + type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), resolve_obligations_buffer: Vec::new(), } @@ -292,14 +289,14 @@ impl<'a> InferenceTable<'a> { } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { + let is_diverging = self + .type_variable_table + .get(iv.index() as usize) + .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)); + if is_diverging { + return TyKind::Never.intern(Interner); + } match kind { - _ if self - .type_variable_table - .get(iv.index() as usize) - .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) => - { - TyKind::Never - } TyVariableKind::General => TyKind::Error, TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), @@ -307,12 +304,9 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize + HasInterner>( - &mut self, - t: T, - ) -> Canonicalized + pub(crate) fn canonicalize_with_free_vars(&mut self, t: T) -> Canonicalized where - T: HasInterner, + T: TypeFoldable + HasInterner, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables @@ -326,6 +320,16 @@ impl<'a> InferenceTable<'a> { Canonicalized { value: result.quantified, free_vars } } + pub(crate) fn canonicalize(&mut self, t: T) -> Canonical + where + T: TypeFoldable + HasInterner, + { + // try to resolve obligations before canonicalizing, since this might + // result in new knowledge about variables + self.resolve_obligations_as_possible(); + self.var_unification_table.canonicalize(Interner, t).quantified + } + /// Recurses through the given type, normalizing associated types mentioned /// in it by replacing them by type variables and registering obligations to /// resolve later. This should be done once for every type we get from some @@ -541,7 +545,7 @@ impl<'a> InferenceTable<'a> { Err(_) => return false, }; result.goals.iter().all(|goal| { - let canonicalized = self.canonicalize(goal.clone()); + let canonicalized = self.canonicalize_with_free_vars(goal.clone()); self.try_resolve_obligation(&canonicalized).is_some() }) } @@ -575,19 +579,15 @@ impl<'a> InferenceTable<'a> { pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot { let var_table_snapshot = self.var_unification_table.snapshot(); - let type_variable_table_snapshot = self.type_variable_table.clone(); + let type_variable_table = self.type_variable_table.clone(); let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot { - var_table_snapshot, - pending_obligations, - type_variable_table_snapshot, - } + InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table } } #[tracing::instrument(skip_all)] pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) { self.var_unification_table.rollback_to(snapshot.var_table_snapshot); - self.type_variable_table = snapshot.type_variable_table_snapshot; + self.type_variable_table = snapshot.type_variable_table; self.pending_obligations = snapshot.pending_obligations; } @@ -606,7 +606,7 @@ impl<'a> InferenceTable<'a> { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env); - self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value) + self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized) } pub(crate) fn register_obligation(&mut self, goal: Goal) { @@ -615,7 +615,7 @@ impl<'a> InferenceTable<'a> { } fn register_obligation_in_env(&mut self, goal: InEnvironment) { - let canonicalized = self.canonicalize(goal); + let canonicalized = self.canonicalize_with_free_vars(goal); let solution = self.try_resolve_obligation(&canonicalized); if matches!(solution, Some(Solution::Ambig(_))) { self.pending_obligations.push(canonicalized); @@ -798,7 +798,7 @@ impl<'a> InferenceTable<'a> { let trait_data = self.db.trait_data(fn_once_trait); let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?; - let mut arg_tys = vec![]; + let mut arg_tys = Vec::with_capacity(num_args); let arg_ty = TyBuilder::tuple(num_args) .fill(|it| { let arg = match it { @@ -828,11 +828,7 @@ impl<'a> InferenceTable<'a> { environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self - .db - .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner)) - .is_some() - { + if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { @@ -845,7 +841,7 @@ impl<'a> InferenceTable<'a> { let canonical = self.canonicalize(obligation.clone()); if self .db - .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner)) + .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) .is_some() { return Some((fn_x, arg_tys, return_ty)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index dea292711d8..9655981cc9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -371,8 +371,8 @@ pub fn layout_of_ty_query( TyKind::Never => cx.layout_of_never_type(), TyKind::Dyn(_) | TyKind::Foreign(_) => { let mut unit = layout_of_unit(&cx, dl)?; - match unit.abi { - Abi::Aggregate { ref mut sized } => *sized = false, + match &mut unit.abi { + Abi::Aggregate { sized } => *sized = false, _ => return Err(LayoutError::Unknown), } unit diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index e68dbe7b02e..a679a114b4b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -213,7 +213,7 @@ impl TraitImpls { // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in module_data.scope.unnamed_consts(db.upcast()) { + for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { Self::collect_def_map(db, map, &block_def_map); @@ -337,7 +337,7 @@ impl InherentImpls { // To better support custom derives, collect impls in all unnamed const items. // const _: () = { ... }; - for konst in module_data.scope.unnamed_consts(db.upcast()) { + for konst in module_data.scope.unnamed_consts() { let body = db.body(konst.into()); for (_, block_def_map) in body.blocks(db.upcast()) { self.collect_def_map(db, &block_def_map); @@ -972,10 +972,9 @@ pub fn iterate_method_candidates_dyn( deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { iterate_method_candidates_with_autoref( - &receiver_ty, + &mut table, + receiver_ty, adj, - db, - env.clone(), traits_in_scope, visible_from_module, name, @@ -1000,10 +999,9 @@ pub fn iterate_method_candidates_dyn( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_with_autoref( - receiver_ty: &Canonical, + table: &mut InferenceTable<'_>, + receiver_ty: Canonical, first_adjustment: ReceiverAdjustments, - db: &dyn HirDatabase, - env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, @@ -1016,10 +1014,9 @@ fn iterate_method_candidates_with_autoref( let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { iterate_method_candidates_by_receiver( + table, receiver_ty, first_adjustment, - db, - env.clone(), traits_in_scope, visible_from_module, name, @@ -1034,7 +1031,7 @@ fn iterate_method_candidates_with_autoref( maybe_reborrowed.autoderefs += 1; } - iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?; + iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; let refed = Canonical { value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) @@ -1042,7 +1039,7 @@ fn iterate_method_candidates_with_autoref( binders: receiver_ty.binders.clone(), }; - iterate_method_candidates_by_receiver(&refed, first_adjustment.with_autoref(Mutability::Not))?; + iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?; let ref_muted = Canonical { value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) @@ -1050,58 +1047,53 @@ fn iterate_method_candidates_with_autoref( binders: receiver_ty.binders.clone(), }; - iterate_method_candidates_by_receiver( - &ref_muted, - first_adjustment.with_autoref(Mutability::Mut), - ) + iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) } #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver( - receiver_ty: &Canonical, + table: &mut InferenceTable<'_>, + receiver_ty: Canonical, receiver_adjustments: ReceiverAdjustments, - db: &dyn HirDatabase, - env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { - let mut table = InferenceTable::new(db, env); let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); - let snapshot = table.snapshot(); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. - let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true); - while let Some((self_ty, _)) = autoderef.next() { - iterate_inherent_methods( - &self_ty, - autoderef.table, - name, - Some(&receiver_ty), - Some(receiver_adjustments.clone()), - visible_from_module, - &mut callback, - )? - } - - table.rollback_to(snapshot); - - let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true); - while let Some((self_ty, _)) = autoderef.next() { - iterate_trait_method_candidates( - &self_ty, - autoderef.table, - traits_in_scope, - name, - Some(&receiver_ty), - Some(receiver_adjustments.clone()), - &mut callback, - )? - } - - ControlFlow::Continue(()) + table.run_in_snapshot(|table| { + let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); + while let Some((self_ty, _)) = autoderef.next() { + iterate_inherent_methods( + &self_ty, + autoderef.table, + name, + Some(&receiver_ty), + Some(receiver_adjustments.clone()), + visible_from_module, + &mut callback, + )? + } + ControlFlow::Continue(()) + })?; + table.run_in_snapshot(|table| { + let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true); + while let Some((self_ty, _)) = autoderef.next() { + iterate_trait_method_candidates( + &self_ty, + autoderef.table, + traits_in_scope, + name, + Some(&receiver_ty), + Some(receiver_adjustments.clone()), + &mut callback, + )? + } + ControlFlow::Continue(()) + }) } #[tracing::instrument(skip_all, fields(name = ?name))] @@ -1147,9 +1139,9 @@ fn iterate_trait_method_candidates( callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; - let env = table.trait_env.clone(); - let canonical_self_ty = table.canonicalize(self_ty.clone()).value; + let canonical_self_ty = table.canonicalize(self_ty.clone()); + let TraitEnvironment { krate, block, .. } = *table.trait_env; 'traits: for &t in traits_in_scope { let data = db.trait_data(t); @@ -1164,7 +1156,7 @@ fn iterate_trait_method_candidates( { // FIXME: this should really be using the edition of the method name's span, in case it // comes from a macro - if db.crate_graph()[env.krate].edition < Edition::Edition2021 { + if db.crate_graph()[krate].edition < Edition::Edition2021 { continue; } } @@ -1183,8 +1175,8 @@ fn iterate_trait_method_candidates( IsValidCandidate::No => continue, }; if !known_implemented { - let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty); - if db.trait_solve(env.krate, env.block, goal.cast(Interner)).is_none() { + let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); + if db.trait_solve(krate, block, goal.cast(Interner)).is_none() { continue 'traits; } } @@ -1365,7 +1357,7 @@ pub(crate) fn resolve_indexing_op( let ty = table.instantiate_canonical(ty); let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { - let goal = generic_implements_goal(db, table.trait_env.clone(), index_trait, &ty); + let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); if db .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) .is_some() @@ -1548,7 +1540,7 @@ fn is_valid_impl_fn_candidate( for goal in goals.clone() { let in_env = InEnvironment::new(&table.trait_env.env, goal); - let canonicalized = table.canonicalize(in_env); + let canonicalized = table.canonicalize_with_free_vars(in_env); let solution = table.db.trait_solve( table.trait_env.krate, table.trait_env.block, @@ -1586,10 +1578,10 @@ fn is_valid_impl_fn_candidate( pub fn implements_trait( ty: &Canonical, db: &dyn HirDatabase, - env: Arc, + env: &TraitEnvironment, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env.clone(), trait_, ty); + let goal = generic_implements_goal(db, env, trait_, ty); let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); solution.is_some() @@ -1598,10 +1590,10 @@ pub fn implements_trait( pub fn implements_trait_unique( ty: &Canonical, db: &dyn HirDatabase, - env: Arc, + env: &TraitEnvironment, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env.clone(), trait_, ty); + let goal = generic_implements_goal(db, env, trait_, ty); let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); matches!(solution, Some(crate::Solution::Unique(_))) @@ -1612,32 +1604,34 @@ pub fn implements_trait_unique( #[tracing::instrument(skip_all)] fn generic_implements_goal( db: &dyn HirDatabase, - env: Arc, + env: &TraitEnvironment, trait_: TraitId, self_ty: &Canonical, ) -> Canonical> { - let mut kinds = self_ty.binders.interned().to_vec(); + let binders = self_ty.binders.interned(); let trait_ref = TyBuilder::trait_ref(db, trait_) .push(self_ty.value.clone()) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) + .fill_with_bound_vars(DebruijnIndex::INNERMOST, binders.len()) .build(); - kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|it| { - let vk = match it.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, - chalk_ir::GenericArgData::Const(c) => { - chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) - } - }; - chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) - })); + + let kinds = + binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { + let vk = match it.data(Interner) { + chalk_ir::GenericArgData::Ty(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, + chalk_ir::GenericArgData::Const(c) => { + chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) + } + }; + chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) + })); + let binders = CanonicalVarKinds::from_iter(Interner, kinds); + let obligation = trait_ref.cast(Interner); - Canonical { - binders: CanonicalVarKinds::from_iter(Interner, kinds), - value: InEnvironment::new(&env.env, obligation), - } + let value = InEnvironment::new(&env.env, obligation); + Canonical { binders, value } } fn autoderef_method_receiver( @@ -1648,7 +1642,7 @@ fn autoderef_method_receiver( let mut autoderef = autoderef::Autoderef::new(table, ty, false); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( - autoderef.table.canonicalize(ty).value, + autoderef.table.canonicalize(ty), ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index cfaef2a392c..d5133550377 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -1165,6 +1165,7 @@ impl MirBody { pub enum MirSpan { ExprId(ExprId), PatId(PatId), + SelfParam, Unknown, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 2428678d72b..fd98141af63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -376,6 +376,10 @@ impl MirEvalError { Ok(s) => s.map(|it| it.syntax_node_ptr()), Err(_) => continue, }, + MirSpan::SelfParam => match source_map.self_param_syntax() { + Some(s) => s.map(|it| it.syntax_node_ptr()), + None => continue, + }, MirSpan::Unknown => continue, }; let file_id = span.file_id.original_file(db.upcast()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index d0f739e6ac6..7e582c03efc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -1810,9 +1810,20 @@ impl<'ctx> MirLowerCtx<'ctx> { fn lower_params_and_bindings( &mut self, params: impl Iterator + Clone, + self_binding: Option<(BindingId, Ty)>, pick_binding: impl Fn(BindingId) -> bool, ) -> Result { let base_param_count = self.result.param_locals.len(); + let self_binding = match self_binding { + Some((self_binding, ty)) => { + let local_id = self.result.locals.alloc(Local { ty }); + self.drop_scopes.last_mut().unwrap().locals.push(local_id); + self.result.binding_locals.insert(self_binding, local_id); + self.result.param_locals.push(local_id); + Some(self_binding) + } + None => None, + }; self.result.param_locals.extend(params.clone().map(|(it, ty)| { let local_id = self.result.locals.alloc(Local { ty }); self.drop_scopes.last_mut().unwrap().locals.push(local_id); @@ -1838,9 +1849,23 @@ impl<'ctx> MirLowerCtx<'ctx> { } } let mut current = self.result.start_block; - for ((param, _), local) in - params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count)) - { + if let Some(self_binding) = self_binding { + let local = self.result.param_locals.clone()[base_param_count]; + if local != self.binding_local(self_binding)? { + let r = self.match_self_param(self_binding, current, local)?; + if let Some(b) = r.1 { + self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam); + } + current = r.0; + } + } + let local_params = self + .result + .param_locals + .clone() + .into_iter() + .skip(base_param_count + self_binding.is_some() as usize); + for ((param, _), local) in params.zip(local_params) { if let Pat::Bind { id, .. } = self.body[param] { if local == self.binding_local(id)? { continue; @@ -2019,6 +2044,7 @@ pub fn mir_body_for_closure_query( }; let current = ctx.lower_params_and_bindings( args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())), + None, |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { @@ -2149,16 +2175,16 @@ pub fn lower_to_mir( let substs = TyBuilder::placeholder_subst(db, fid); let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs); + let mut params = callable_sig.params().iter(); + let self_param = body.self_param.and_then(|id| Some((id, params.next()?.clone()))); break 'b ctx.lower_params_and_bindings( - body.params - .iter() - .zip(callable_sig.params().iter()) - .map(|(it, y)| (*it, y.clone())), + body.params.iter().zip(params).map(|(it, y)| (*it, y.clone())), + self_param, binding_picker, )?; } } - ctx.lower_params_and_bindings([].into_iter(), binding_picker)? + ctx.lower_params_and_bindings([].into_iter(), None, binding_picker)? }; if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 90cbd13a6c6..75969067943 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -11,7 +11,7 @@ use crate::{ Substitution, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind, ValueNs, VariantData, VariantId, }, - MutBorrowKind, + LocalId, MutBorrowKind, }, BindingMode, }; @@ -82,6 +82,22 @@ impl MirLowerCtx<'_> { Ok((current, current_else)) } + pub(super) fn match_self_param( + &mut self, + id: BindingId, + current: BasicBlockId, + local: LocalId, + ) -> Result<(BasicBlockId, Option)> { + self.pattern_match_binding( + id, + BindingMode::Move, + local.into(), + MirSpan::SelfParam, + current, + None, + ) + } + fn pattern_match_inner( &mut self, mut current: BasicBlockId, @@ -283,9 +299,9 @@ impl MirLowerCtx<'_> { (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } - if let Some(slice) = slice { + if let &Some(slice) = slice { if mode == MatchingMode::Bind { - if let Pat::Bind { id, subpat: _ } = self.body[*slice] { + if let Pat::Bind { id, subpat: _ } = self.body[slice] { let next_place = cond_place.project( ProjectionElem::Subslice { from: prefix.len() as u64, @@ -293,11 +309,12 @@ impl MirLowerCtx<'_> { }, &mut self.result.projection_store, ); + let mode = self.infer.binding_modes[slice]; (current, current_else) = self.pattern_match_binding( id, - *slice, + mode, next_place, - (*slice).into(), + (slice).into(), current, current_else, )?; @@ -398,9 +415,10 @@ impl MirLowerCtx<'_> { self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)? } if mode == MatchingMode::Bind { + let mode = self.infer.binding_modes[pattern]; self.pattern_match_binding( *id, - pattern, + mode, cond_place, pattern.into(), current, @@ -437,14 +455,13 @@ impl MirLowerCtx<'_> { fn pattern_match_binding( &mut self, id: BindingId, - pat: PatId, + mode: BindingMode, cond_place: Place, span: MirSpan, current: BasicBlockId, current_else: Option, ) -> Result<(BasicBlockId, Option)> { let target_place = self.binding_local(id)?; - let mode = self.infer.binding_modes[pat]; self.push_storage_live(id, current)?; self.push_assignment( current, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 5e159236f48..d699067b5a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -12,7 +12,7 @@ mod traits; use std::env; -use base_db::{FileRange, SourceDatabaseExt}; +use base_db::{FileRange, SourceDatabaseExt2 as _}; use expect_test::Expect; use hir_def::{ body::{Body, BodySourceMap, SyntheticSyntax}, @@ -164,7 +164,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour Some(value) => value, None => continue, }; - let range = node.as_ref().original_file_range(&db); + let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { let actual = if display_source { ty.display_source_code(&db, def.module(&db), true).unwrap() @@ -180,7 +180,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour Some(value) => value, None => continue, }; - let range = node.as_ref().original_file_range(&db); + let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { let actual = if display_source { ty.display_source_code(&db, def.module(&db), true).unwrap() @@ -211,7 +211,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour }) else { continue; }; - let range = node.as_ref().original_file_range(&db); + let range = node.as_ref().original_file_range_rooted(&db); let actual = format!( "expected {}, got {}", mismatch.expected.display_test(&db), @@ -293,20 +293,29 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut types: Vec<(InFile, &Ty)> = Vec::new(); let mut mismatches: Vec<(InFile, &TypeMismatch)> = Vec::new(); + if let Some(self_param) = body.self_param { + let ty = &inference_result.type_of_binding[self_param]; + if let Some(syntax_ptr) = body_source_map.self_param_syntax() { + let root = db.parse_or_expand(syntax_ptr.file_id); + let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); + types.push((node.clone(), ty)); + } + } + for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { ty = &inference_result.type_of_binding[id]; } - let syntax_ptr = match body_source_map.pat_syntax(pat) { + let node = match body_source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, }; - types.push((syntax_ptr.clone(), ty)); + types.push((node.clone(), ty)); if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) { - mismatches.push((syntax_ptr, mismatch)); + mismatches.push((node, mismatch)); } } @@ -575,7 +584,7 @@ fn salsa_bug() { } "; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 82d934009f3..6066ec69c9a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -1,6 +1,5 @@ -use base_db::SourceDatabaseExt; +use base_db::SourceDatabaseExt2 as _; use test_fixture::WithFixture; -use triomphe::Arc; use crate::{db::HirDatabase, test_db::TestDB}; @@ -33,7 +32,7 @@ fn foo() -> i32 { 1 }"; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); { let events = db.log_executed(|| { @@ -85,7 +84,7 @@ fn baz() -> i32 { } "; - db.set_file_text(pos.file_id, Arc::from(new_text)); + db.set_file_text(pos.file_id, new_text); { let events = db.log_executed(|| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index c837fae3fef..8609ba41039 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1461,28 +1461,6 @@ fn f() { ); } -#[test] -fn trait_impl_in_synstructure_const() { - check_types( - r#" -struct S; - -trait Tr { - fn method(&self) -> u16; -} - -const _DERIVE_Tr_: () = { - impl Tr for S {} -}; - -fn f() { - S.method(); - //^^^^^^^^^^ u16 -} - "#, - ); -} - #[test] fn inherent_impl_in_unnamed_const() { check_types( @@ -1795,6 +1773,21 @@ fn test() { ); } +#[test] +fn deref_into_inference_var() { + check_types( + r#" +//- minicore:deref +struct A(T); +impl core::ops::Deref for A {} +impl A { fn foo(&self) {} } +fn main() { + A(0).foo(); + //^^^^^^^^^^ () +} +"#, + ); +} #[test] fn receiver_adjustment_autoref() { check( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index ffd6a6051b9..917e9f44085 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2121,6 +2121,7 @@ async fn main() { "#, expect![[r#" 16..193 '{ ...2 }; }': () + 16..193 '{ ...2 }; }': impl Future 26..27 'x': i32 30..43 'unsafe { 92 }': i32 39..41 '92': i32 @@ -2131,6 +2132,8 @@ async fn main() { 73..75 '()': () 95..96 'z': ControlFlow<(), ()> 130..140 'try { () }': ControlFlow<(), ()> + 130..140 'try { () }': fn from_output>( as Try>::Output) -> ControlFlow<(), ()> + 130..140 'try { () }': ControlFlow<(), ()> 136..138 '()': () 150..151 'w': i32 154..166 'const { 92 }': i32 diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index fa9fe4953ed..4518422d27e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -204,7 +204,7 @@ pub struct NoSuchField { #[derive(Debug)] pub struct PrivateAssocItem { - pub expr_or_pat: InFile>>>, + pub expr_or_pat: InFile>>, pub item: AssocItem, } @@ -240,7 +240,7 @@ pub struct UnresolvedMethodCall { #[derive(Debug)] pub struct UnresolvedAssocItem { - pub expr_or_pat: InFile>>>, + pub expr_or_pat: InFile>>, } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index cdc0db8653c..c5d44c11f2c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -159,6 +159,7 @@ impl HirDisplay for Adt { impl HirDisplay for Struct { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { let module_id = self.module(f.db).id; + // FIXME: Render repr if its set explicitly? write_visibility(module_id, self.visibility(f.db), f)?; f.write_str("struct ")?; write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; @@ -166,37 +167,40 @@ impl HirDisplay for Struct { write_generic_params(def_id, f)?; let variant_data = self.variant_data(f.db); - if let StructKind::Tuple = variant_data.kind() { - f.write_char('(')?; - let mut it = variant_data.fields().iter().peekable(); + match variant_data.kind() { + StructKind::Tuple => { + f.write_char('(')?; + let mut it = variant_data.fields().iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; - write_visibility(module_id, field.visibility(f.db), f)?; - field.ty(f.db).hir_fmt(f)?; - if it.peek().is_some() { - f.write_str(", ")?; + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; + write_visibility(module_id, field.visibility(f.db), f)?; + field.ty(f.db).hir_fmt(f)?; + if it.peek().is_some() { + f.write_str(", ")?; + } + } + + f.write_char(')')?; + write_where_clause(def_id, f)?; + } + StructKind::Record => { + let has_where_clause = write_where_clause(def_id, f)?; + let fields = self.fields(f.db); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{\n")?; + for field in self.fields(f.db) { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + f.write_str("}")?; } } - - f.write_str(");")?; - } - - write_where_clause(def_id, f)?; - - if let StructKind::Record = variant_data.kind() { - let fields = self.fields(f.db); - if fields.is_empty() { - f.write_str(" {}")?; - } else { - f.write_str(" {\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; - } - f.write_str("}")?; - } + StructKind::Unit => _ = write_where_clause(def_id, f)?, } Ok(()) @@ -210,11 +214,12 @@ impl HirDisplay for Enum { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; let variants = self.variants(f.db); if !variants.is_empty() { - f.write_str(" {\n")?; + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + f.write_str("{\n")?; for variant in variants { f.write_str(" ")?; variant.hir_fmt(f)?; @@ -234,11 +239,12 @@ impl HirDisplay for Union { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; let fields = self.fields(f.db); if !fields.is_empty() { - f.write_str(" {\n")?; + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + f.write_str("{\n")?; for field in self.fields(f.db) { f.write_str(" ")?; field.hir_fmt(f)?; @@ -446,7 +452,10 @@ fn write_generic_params( Ok(()) } -fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { +fn write_where_clause( + def: GenericDefId, + f: &mut HirFormatter<'_>, +) -> Result { let params = f.db.generic_params(def); // unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`. @@ -465,7 +474,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), }); if !has_displayable_predicate { - return Ok(()); + return Ok(false); } let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target { @@ -543,7 +552,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), // End of final predicate. There must be at least one predicate here. f.write_char(',')?; - Ok(()) + Ok(true) } impl HirDisplay for Const { @@ -594,19 +603,20 @@ impl HirDisplay for Trait { write!(f, "trait {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TraitId(self.id); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { let assoc_items = self.items(f.db); let count = assoc_items.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; if count == 0 { if assoc_items.is_empty() { - f.write_str(" {}")?; + f.write_str("{}")?; } else { - f.write_str(" { /* … */ }")?; + f.write_str("{ /* … */ }")?; } } else { - f.write_str(" {\n")?; + f.write_str("{\n")?; for item in &assoc_items[..count] { f.write_str(" ")?; match item { @@ -651,7 +661,6 @@ impl HirDisplay for TypeAlias { write!(f, "type {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TypeAliasId(self.id); write_generic_params(def_id, f)?; - write_where_clause(def_id, f)?; if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined(data.bounds.iter(), " + ")?; @@ -660,6 +669,7 @@ impl HirDisplay for TypeAlias { f.write_str(" = ")?; ty.hir_fmt(f)?; } + write_where_clause(def_id, f)?; Ok(()) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index d10884517f9..7cdcdd76d18 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -9,6 +9,7 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile}; use syntax::ast; +use tt::TextRange; use crate::{ db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, @@ -37,6 +38,12 @@ impl Module { def_map[self.id.local_id].definition_source(db.upcast()) } + /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. + pub fn definition_source_range(self, db: &dyn HirDatabase) -> InFile { + let def_map = self.id.def_map(db.upcast()); + def_map[self.id.local_id].definition_source_range(db.upcast()) + } + pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].definition_source_file_id() @@ -71,6 +78,13 @@ impl Module { let def_map = self.id.def_map(db.upcast()); def_map[self.id.local_id].declaration_source(db.upcast()) } + + /// Returns a text range which declares this module, either a `mod foo;` or a `mod foo {}`. + /// `None` for the crate root. + pub fn declaration_source_range(self, db: &dyn HirDatabase) -> Option> { + let def_map = self.id.def_map(db.upcast()); + def_map[self.id.local_id].declaration_source_range(db.upcast()) + } } impl HasSource for Field { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 5eed7ecd5b2..b922aa8e46d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -56,8 +56,8 @@ use hir_def::{ AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, - MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, - TypeOrConstParamId, TypeParamId, UnionId, + ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId, + TypeParamId, UnionId, }; use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; use hir_ty::{ @@ -122,7 +122,7 @@ pub use { visibility::Visibility, // FIXME: This is here since some queries take it as input that are used // outside of hir. - {AdtId, ModuleDefId}, + {AdtId, MacroId, ModuleDefId}, }, hir_expand::{ attrs::{Attr, AttrId}, @@ -754,7 +754,7 @@ impl Module { scope .declarations() .map(ModuleDef::from) - .chain(scope.unnamed_consts(db.upcast()).map(|id| ModuleDef::Const(Const::from(id)))) + .chain(scope.unnamed_consts().map(|id| ModuleDef::Const(Const::from(id)))) .collect() } @@ -1725,6 +1725,10 @@ impl DefWithBody { Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, + mir::MirSpan::SelfParam => match source_map.self_param_syntax() { + Some(s) => s.map(|it| it.into()), + None => continue, + }, mir::MirSpan::Unknown => continue, }; acc.push( @@ -1776,6 +1780,11 @@ impl DefWithBody { Ok(s) => s.map(|it| it.into()), Err(_) => continue, }, + mir::MirSpan::SelfParam => match source_map.self_param_syntax() + { + Some(s) => s.map(|it| it.into()), + None => continue, + }, mir::MirSpan::Unknown => continue, }; acc.push(NeedMut { local, span }.into()); @@ -2127,8 +2136,11 @@ impl Param { pub fn as_local(&self, db: &dyn HirDatabase) -> Option { let parent = DefWithBodyId::FunctionId(self.func.into()); let body = db.body(parent); - let pat_id = body.params[self.idx]; - if let Pat::Bind { id, .. } = &body[pat_id] { + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { + Some(Local { parent, binding_id: self_param }) + } else if let Pat::Bind { id, .. } = + &body[body.params[self.idx - body.self_param.is_some() as usize]] + { Some(Local { parent, binding_id: *id }) } else { None @@ -2143,7 +2155,7 @@ impl Param { let InFile { file_id, value } = self.func.source(db)?; let params = value.param_list()?; if params.self_param().is_some() { - params.params().nth(self.idx.checked_sub(1)?) + params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?) } else { params.params().nth(self.idx) } @@ -2605,6 +2617,15 @@ impl Macro { } } + pub fn is_env_or_option_env(&self, db: &dyn HirDatabase) -> bool { + match self.id { + MacroId::Macro2Id(it) => { + matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env()) + } + MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false, + } + } + pub fn is_attr(&self, db: &dyn HirDatabase) -> bool { matches!(self.kind(db), MacroKind::Attr) } @@ -3134,35 +3155,59 @@ impl Local { /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` pub fn sources(self, db: &dyn HirDatabase) -> Vec { let (body, source_map) = db.body_with_source_map(self.parent); - self.sources_(db, &body, &source_map).collect() + match body.self_param.zip(source_map.self_param_syntax()) { + Some((param, source)) if param == self.binding_id => { + let root = source.file_syntax(db.upcast()); + vec![LocalSource { + local: self, + source: source.map(|ast| Either::Right(ast.to_node(&root))), + }] + } + _ => body[self.binding_id] + .definitions + .iter() + .map(|&definition| { + let src = source_map.pat_syntax(definition).unwrap(); // Hmm... + let root = src.file_syntax(db.upcast()); + LocalSource { + local: self, + source: src.map(|ast| match ast.to_node(&root) { + ast::Pat::IdentPat(it) => Either::Left(it), + _ => unreachable!("local with non ident-pattern"), + }), + } + }) + .collect(), + } } /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = it;` pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource { let (body, source_map) = db.body_with_source_map(self.parent); - let src = self.sources_(db, &body, &source_map).next().unwrap(); - src - } - - fn sources_<'a>( - self, - db: &'a dyn HirDatabase, - body: &'a hir_def::body::Body, - source_map: &'a hir_def::body::BodySourceMap, - ) -> impl Iterator + 'a { - body[self.binding_id] - .definitions - .iter() - .map(|&definition| { - let src = source_map.pat_syntax(definition).unwrap(); // Hmm... - let root = src.file_syntax(db.upcast()); - src.map(|ast| match ast.to_node(&root) { - Either::Left(ast::Pat::IdentPat(it)) => Either::Left(it), - Either::Left(_) => unreachable!("local with non ident-pattern"), - Either::Right(it) => Either::Right(it), + match body.self_param.zip(source_map.self_param_syntax()) { + Some((param, source)) if param == self.binding_id => { + let root = source.file_syntax(db.upcast()); + LocalSource { + local: self, + source: source.map(|ast| Either::Right(ast.to_node(&root))), + } + } + _ => body[self.binding_id] + .definitions + .first() + .map(|&definition| { + let src = source_map.pat_syntax(definition).unwrap(); // Hmm... + let root = src.file_syntax(db.upcast()); + LocalSource { + local: self, + source: src.map(|ast| match ast.to_node(&root) { + ast::Pat::IdentPat(it) => Either::Left(it), + _ => unreachable!("local with non ident-pattern"), + }), + } }) - }) - .map(move |source| LocalSource { local: self, source }) + .unwrap(), + } } } @@ -4037,7 +4082,7 @@ impl Type { let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_) + method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. @@ -4052,12 +4097,7 @@ impl Type { let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait_unique( - &canonical_ty, - db, - self.env.clone(), - fnonce_trait, - ) + method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, fnonce_trait) } // FIXME: Find better API that also handles const generics diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 99907ea15b5..9796009cb45 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -681,28 +681,29 @@ impl<'db> SemanticsImpl<'db> { .filter(|&(_, include_file_id)| include_file_id == file_id) { let macro_file = invoc.as_macro_file(); - let expansion_info = cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let expansion_info = cache.entry(macro_file).or_insert_with(|| { + let exp_info = macro_file.expansion_info(self.db.upcast()); + + let InMacroFile { file_id, value } = exp_info.expanded(); + self.cache(value, file_id.into()); + + exp_info + }); // Create the source analyzer for the macro call scope let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file())) else { continue; }; - { - let InMacroFile { file_id: macro_file, value } = expansion_info.expanded(); - self.cache(value, macro_file.into()); - } // get mapped token in the include! macro file - let span = span::SpanData { + let span = span::Span { range: token.text_range(), anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, ctx: SyntaxContextId::ROOT, }; let Some(InMacroFile { file_id, value: mut mapped_tokens }) = - expansion_info.map_range_down(span) + expansion_info.map_range_down_exact(span) else { continue; }; @@ -753,22 +754,20 @@ impl<'db> SemanticsImpl<'db> { let def_map = sa.resolver.def_map(); let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])]; - let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| { - let expansion_info = cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let exp_info = cache.entry(macro_file).or_insert_with(|| { + let exp_info = macro_file.expansion_info(self.db.upcast()); - { - let InMacroFile { file_id, value } = expansion_info.expanded(); + let InMacroFile { file_id, value } = exp_info.expanded(); self.cache(value, file_id.into()); - } - let InMacroFile { file_id, value: mapped_tokens } = - expansion_info.map_range_down(span)?; + exp_info + }); + + let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?; let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect(); - // if the length changed we have found a mapping for the token + // we have found a mapping for the token if the vec is non-empty let res = mapped_tokens.is_empty().not().then_some(()); // requeue the tokens we got from mapping our current token down stack.push((HirFileId::from(file_id), mapped_tokens)); @@ -851,7 +850,13 @@ impl<'db> SemanticsImpl<'db> { // remove any other token in this macro input, all their mappings are the // same as this one tokens.retain(|t| !text_range.contains_range(t.text_range())); - process_expansion_for_token(&mut stack, file_id) + + process_expansion_for_token(&mut stack, file_id).or(file_id + .eager_arg(self.db.upcast()) + .and_then(|arg| { + // also descend into eager expansions + process_expansion_for_token(&mut stack, arg.as_macro_file()) + })) } else if let Some(meta) = ast::Meta::cast(parent) { // attribute we failed expansion for earlier, this might be a derive invocation // or derive helper attribute @@ -960,7 +965,7 @@ impl<'db> SemanticsImpl<'db> { /// macro file the node resides in. pub fn original_range(&self, node: &SyntaxNode) -> FileRange { let node = self.find_file(node); - node.original_file_range(self.db.upcast()) + node.original_file_range_rooted(self.db.upcast()) } /// Attempts to map the node out of macro expanded files returning the original file range. @@ -984,9 +989,9 @@ impl<'db> SemanticsImpl<'db> { /// Attempts to map the node out of macro expanded files. /// This only work for attribute expansions, as other ones do not have nodes as input. - pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option { + pub fn original_syntax_node_rooted(&self, node: &SyntaxNode) -> Option { let InFile { file_id, .. } = self.find_file(node); - InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map( + InFile::new(file_id, node).original_syntax_node_rooted(self.db.upcast()).map( |InRealFile { file_id, value }| { self.cache(find_root(&value), file_id.into()); value @@ -997,7 +1002,7 @@ impl<'db> SemanticsImpl<'db> { pub fn diagnostics_display_range(&self, src: InFile) -> FileRange { let root = self.parse_or_expand(src.file_id); let node = src.map(|it| it.to_node(&root)); - node.as_ref().original_file_range(self.db.upcast()) + node.as_ref().original_file_range_rooted(self.db.upcast()) } fn token_ancestors_with_macros( @@ -1236,6 +1241,11 @@ impl<'db> SemanticsImpl<'db> { sa.resolve_macro_call(self.db, macro_call) } + pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool { + self.resolve_macro_call(macro_call) + .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..))) + } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let sa = match self.analyze(macro_call.syntax()) { Some(it) => it, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 4733ea5a35b..d4d6f0b243f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -101,7 +101,7 @@ use hir_def::{ use hir_expand::{attrs::AttrId, name::AsName, HirFileId, HirFileIdExt, MacroCallId}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use stdx::{impl_from, never}; +use stdx::impl_from; use syntax::{ ast::{self, HasName}, AstNode, SyntaxNode, @@ -253,14 +253,8 @@ impl SourceToDefCtx<'_, '_> { src: InFile, ) -> Option<(DefWithBodyId, BindingId)> { let container = self.find_pat_or_label_container(src.syntax())?; - let (body, source_map) = self.db.body_with_source_map(container); - let pat_id = source_map.node_self_param(src.as_ref())?; - if let crate::Pat::Bind { id, .. } = body[pat_id] { - Some((container, id)) - } else { - never!(); - None - } + let body = self.db.body(container); + Some((container, body.self_param?)) } pub(super) fn label_to_def( &mut self, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index f87e0a3897a..dc96a1b03d0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -219,11 +219,10 @@ impl SourceAnalyzer { pub(crate) fn type_of_self( &self, db: &dyn HirDatabase, - param: &ast::SelfParam, + _param: &ast::SelfParam, ) -> Option { - let src = InFile { file_id: self.file_id, value: param }; - let pat_id = self.body_source_map()?.node_self_param(src)?; - let ty = self.infer.as_ref()?[pat_id].clone(); + let binding = self.body()?.self_param?; + let ty = self.infer.as_ref()?[binding].clone(); Some(Type::new_with_resolver(db, &self.resolver, ty)) } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 28ac5940e69..3b88836c24b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -49,7 +49,7 @@ impl DeclarationLocation { return FileRange { file_id, range: self.ptr.text_range() }; } let node = resolve_node(db, self.hir_file_id, &self.ptr); - node.as_ref().original_file_range(db.upcast()) + node.as_ref().original_file_range_rooted(db.upcast()) } } @@ -165,7 +165,6 @@ impl<'a> SymbolCollector<'a> { // Record renamed imports. // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily // for now. - // FIXME: This parses! for id in scope.imports() { let source = id.import.child_source(self.db.upcast()); let Some(use_tree_src) = source.value.get(id.idx) else { continue }; @@ -196,7 +195,7 @@ impl<'a> SymbolCollector<'a> { }); } - for const_id in scope.unnamed_consts(self.db.upcast()) { + for const_id in scope.unnamed_consts() { self.collect_from_body(const_id); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index af834c8a53d..42f935651cf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -1,5 +1,6 @@ use std::iter; +use either::Either; use hir::{HasSource, HirFileIdExt, ModuleSource}; use ide_db::{ assists::{AssistId, AssistKind}, @@ -10,17 +11,16 @@ use ide_db::{ }; use itertools::Itertools; use smallvec::SmallVec; -use stdx::format_to; use syntax::{ algo::find_node_at_range, ast::{ self, edit::{AstNodeEdit, IndentLevel}, - make, HasName, HasVisibility, + make, HasVisibility, }, - match_ast, ted, AstNode, SourceFile, + match_ast, ted, AstNode, SyntaxKind::{self, WHITESPACE}, - SyntaxNode, TextRange, + SyntaxNode, TextRange, TextSize, }; use crate::{AssistContext, Assists}; @@ -109,76 +109,35 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti //We are getting item usages and record_fields together, record_fields //for change_visibility and usages for first point mentioned above in the process - let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx); + + let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = + module.get_usages_and_record_fields(ctx); + + builder.edit_file(ctx.file_id()); + use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { + builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); + }); let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let mut body_items: Vec = Vec::new(); - let mut items_to_be_processed: Vec = module.body_items.clone(); + let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent); - let new_item_indent = if impl_parent.is_some() { - old_item_indent + 2 - } else { - items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat(); - old_item_indent + 1 - }; - - for item in items_to_be_processed { - let item = item.indent(IndentLevel(1)); - let mut indented_item = String::new(); - format_to!(indented_item, "{new_item_indent}{item}"); - body_items.push(indented_item); - } - - let mut body = body_items.join("\n\n"); - - if let Some(impl_) = &impl_parent { - let mut impl_body_def = String::new(); - - if let Some(self_ty) = impl_.self_ty() { - { - let impl_indent = old_item_indent + 1; - format_to!( - impl_body_def, - "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}", - ); - } - body = impl_body_def; - - // Add the import for enum/struct corresponding to given impl block - module.make_use_stmt_of_node_with_super(self_ty.syntax()); - for item in module.use_items { - let item_indent = old_item_indent + 1; - body = format!("{item_indent}{item}\n\n{body}"); - } - } - } - - let mut module_def = String::new(); - - let module_name = module.name; - format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}"); - - let mut usages_to_be_updated_for_curr_file = vec![]; - for usages_to_be_updated_for_file in usages_to_be_processed { - if usages_to_be_updated_for_file.0 == ctx.file_id() { - usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1; + let mut usages_to_be_processed_for_cur_file = vec![]; + for (file_id, usages) in usages_to_be_processed { + if file_id == ctx.file_id() { + usages_to_be_processed_for_cur_file = usages; continue; } - builder.edit_file(usages_to_be_updated_for_file.0); - for usage_to_be_processed in usages_to_be_updated_for_file.1 { - builder.replace(usage_to_be_processed.0, usage_to_be_processed.1) + builder.edit_file(file_id); + for (text_range, usage) in usages { + builder.replace(text_range, usage) } } builder.edit_file(ctx.file_id()); - for usage_to_be_processed in usages_to_be_updated_for_curr_file { - builder.replace(usage_to_be_processed.0, usage_to_be_processed.1) - } - - for import_path_text_range in import_paths_to_be_removed { - builder.delete(import_path_text_range); + for (text_range, usage) in usages_to_be_processed_for_cur_file { + builder.replace(text_range, usage); } if let Some(impl_) = impl_parent { @@ -199,12 +158,51 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { + for import_path_text_range in import_paths_to_be_removed { + if module.text_range.intersect(import_path_text_range).is_some() { + module.text_range = module.text_range.cover(import_path_text_range); + } else { + builder.delete(import_path_text_range); + } + } + builder.replace(module.text_range, module_def) } }, ) } +fn generate_module_def( + parent_impl: &Option, + module: &mut Module, + old_indent: IndentLevel, +) -> String { + let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() { + (Either::Left(module.body_items.iter()), old_indent + 2) + } else { + (Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1) + }; + + let mut body = items_to_be_processed + .map(|item| item.indent(IndentLevel(1))) + .map(|item| format!("{new_item_indent}{item}")) + .join("\n\n"); + + if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let impl_indent = old_indent + 1; + body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}"); + + // Add the import for enum/struct corresponding to given impl block + module.make_use_stmt_of_node_with_super(self_ty.syntax()); + for item in module.use_items.iter() { + body = format!("{impl_indent}{item}\n\n{body}"); + } + } + + let module_name = module.name; + format!("mod {module_name} {{\n{body}\n{old_indent}}}") +} + #[derive(Debug)] struct Module { text_range: TextRange, @@ -233,20 +231,24 @@ impl Module { fn get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, - ) -> (FxHashMap>, Vec) { + ) -> (FxHashMap>, Vec, FxHashMap) + { let mut adt_fields = Vec::new(); let mut refs: FxHashMap> = FxHashMap::default(); + // use `TextSize` as key to avoid repeated use stmts + let mut use_stmts_to_be_inserted = FxHashMap::default(); //Here impl is not included as each item inside impl will be tied to the parent of //implementing block(a struct, enum, etc), if the parent is in selected module, it will //get updated by ADT section given below or if it is not, then we dont need to do any operation + for item in &self.body_items { match_ast! { match (item.syntax()) { ast::Adt(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Adt(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); //Enum Fields are not allowed to explicitly specify pub, it is implied match it { @@ -280,30 +282,30 @@ impl Module { ast::TypeAlias(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::TypeAlias(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Const(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Const(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Static(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Static(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Fn(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Function(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs); + self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Macro(it) => { if let Some(nod) = ctx.sema.to_def(&it) { - self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs); + self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, _ => (), @@ -311,7 +313,7 @@ impl Module { } } - (refs, adt_fields) + (refs, adt_fields, use_stmts_to_be_inserted) } fn expand_and_group_usages_file_wise( @@ -319,49 +321,62 @@ impl Module { ctx: &AssistContext<'_>, node_def: Definition, refs_in_files: &mut FxHashMap>, + use_stmts_to_be_inserted: &mut FxHashMap, ) { - for (file_id, references) in node_def.usages(&ctx.sema).all() { + let mod_name = self.name; + let covering_node = match ctx.covering_element() { + syntax::NodeOrToken::Node(node) => node, + syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic + }; + let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range()); + let mut use_stmts_set = FxHashSet::default(); + + for (file_id, refs) in node_def.usages(&ctx.sema).all() { let source_file = ctx.sema.parse(file_id); - let usages_in_file = references - .into_iter() - .filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage)); - refs_in_files.entry(file_id).or_default().extend(usages_in_file); - } - } + let usages = refs.into_iter().filter_map(|FileReference { range, .. }| { + // handle normal usages + let name_ref = find_node_at_range::(source_file.syntax(), range)?; - fn get_usage_to_be_processed( - &self, - source_file: &SourceFile, - FileReference { range, name, .. }: FileReference, - ) -> Option<(TextRange, String)> { - let path: ast::Path = find_node_at_range(source_file.syntax(), range)?; - - for desc in path.syntax().descendants() { - if desc.to_string() == name.syntax().to_string() - && !self.text_range.contains_range(desc.text_range()) - { - if let Some(name_ref) = ast::NameRef::cast(desc) { - let mod_name = self.name; - return Some(( - name_ref.syntax().text_range(), - format!("{mod_name}::{name_ref}"), - )); + if out_of_sel(name_ref.syntax()) { + let new_ref = format!("{mod_name}::{name_ref}"); + return Some((range, new_ref)); + } else if let Some(use_) = name_ref.syntax().ancestors().find_map(ast::Use::cast) { + // handle usages in use_stmts which is in_sel + // check if `use` is top stmt in selection + if use_.syntax().parent().is_some_and(|parent| parent == covering_node) + && use_stmts_set.insert(use_.syntax().text_range().start()) + { + let use_ = use_stmts_to_be_inserted + .entry(use_.syntax().text_range().start()) + .or_insert_with(|| use_.clone_subtree().clone_for_update()); + for seg in use_ + .syntax() + .descendants() + .filter_map(ast::NameRef::cast) + .filter(|seg| seg.syntax().to_string() == name_ref.to_string()) + { + let new_ref = make::path_from_text(&format!("{mod_name}::{seg}")) + .clone_for_update(); + ted::replace(seg.syntax().parent()?, new_ref.syntax()); + } + } } - } - } - None + None + }); + refs_in_files.entry(file_id).or_default().extend(usages); + } } fn change_visibility(&mut self, record_fields: Vec) { let (mut replacements, record_field_parents, impls) = get_replacements_for_visibility_change(&mut self.body_items, false); - let mut impl_items: Vec = impls + let mut impl_items = impls .into_iter() .flat_map(|impl_| impl_.syntax().descendants()) .filter_map(ast::Item::cast) - .collect(); + .collect_vec(); let (mut impl_item_replacements, _, _) = get_replacements_for_visibility_change(&mut impl_items, true); @@ -394,133 +409,88 @@ impl Module { fn resolve_imports( &mut self, - curr_parent_module: Option, + module: Option, ctx: &AssistContext<'_>, ) -> Vec { - let mut import_paths_to_be_removed: Vec = vec![]; - let mut node_set: FxHashSet = FxHashSet::default(); + let mut imports_to_remove = vec![]; + let mut node_set = FxHashSet::default(); for item in self.body_items.clone() { - for x in item.syntax().descendants() { - if let Some(name) = ast::Name::cast(x.clone()) { - if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) { - //Necessary to avoid two same names going through - if !node_set.contains(&name.syntax().to_string()) { - node_set.insert(name.syntax().to_string()); - let def_opt: Option = match name_classify { - NameClass::Definition(def) => Some(def), - _ => None, - }; - - if let Some(def) = def_opt { - if let Some(import_path) = self - .process_names_and_namerefs_for_import_resolve( - def, - name.syntax(), - &curr_parent_module, - ctx, - ) - { - check_intersection_and_push( - &mut import_paths_to_be_removed, - import_path, - ); - } - } + item.syntax() + .descendants() + .filter_map(|x| { + if let Some(name) = ast::Name::cast(x.clone()) { + NameClass::classify(&ctx.sema, &name).and_then(|nc| match nc { + NameClass::Definition(def) => Some((name.syntax().clone(), def)), + _ => None, + }) + } else if let Some(name_ref) = ast::NameRef::cast(x) { + NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc { + NameRefClass::Definition(def) => Some((name_ref.syntax().clone(), def)), + _ => None, + }) + } else { + None + } + }) + .for_each(|(node, def)| { + if node_set.insert(node.to_string()) { + if let Some(import) = self.process_def_in_sel(def, &node, &module, ctx) { + check_intersection_and_push(&mut imports_to_remove, import); } } - } - - if let Some(name_ref) = ast::NameRef::cast(x) { - if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) { - //Necessary to avoid two same names going through - if !node_set.contains(&name_ref.syntax().to_string()) { - node_set.insert(name_ref.syntax().to_string()); - let def_opt: Option = match name_classify { - NameRefClass::Definition(def) => Some(def), - _ => None, - }; - - if let Some(def) = def_opt { - if let Some(import_path) = self - .process_names_and_namerefs_for_import_resolve( - def, - name_ref.syntax(), - &curr_parent_module, - ctx, - ) - { - check_intersection_and_push( - &mut import_paths_to_be_removed, - import_path, - ); - } - } - } - } - } - } + }) } - import_paths_to_be_removed + imports_to_remove } - fn process_names_and_namerefs_for_import_resolve( + fn process_def_in_sel( &mut self, def: Definition, - node_syntax: &SyntaxNode, + use_node: &SyntaxNode, curr_parent_module: &Option, ctx: &AssistContext<'_>, ) -> Option { //We only need to find in the current file let selection_range = ctx.selection_trimmed(); - let curr_file_id = ctx.file_id(); - let search_scope = SearchScope::single_file(curr_file_id); - let usage_res = def.usages(&ctx.sema).in_scope(&search_scope).all(); - let file = ctx.sema.parse(curr_file_id); + let file_id = ctx.file_id(); + let usage_res = def.usages(&ctx.sema).in_scope(&SearchScope::single_file(file_id)).all(); + let file = ctx.sema.parse(file_id); - let mut exists_inside_sel = false; - let mut exists_outside_sel = false; - for (_, refs) in usage_res.iter() { - let mut non_use_nodes_itr = refs.iter().filter_map(|x| { - if find_node_at_range::(file.syntax(), x.range).is_none() { - let path_opt = find_node_at_range::(file.syntax(), x.range); - return path_opt; - } - - None - }); - - if non_use_nodes_itr - .clone() - .any(|x| !selection_range.contains_range(x.syntax().text_range())) + // track uses which does not exists in `Use` + let mut uses_exist_in_sel = false; + let mut uses_exist_out_sel = false; + 'outside: for (_, refs) in usage_res.iter() { + for x in refs + .iter() + .filter(|x| find_node_at_range::(file.syntax(), x.range).is_none()) + .filter_map(|x| find_node_at_range::(file.syntax(), x.range)) { - exists_outside_sel = true; - } - if non_use_nodes_itr.any(|x| selection_range.contains_range(x.syntax().text_range())) { - exists_inside_sel = true; + let in_selection = selection_range.contains_range(x.syntax().text_range()); + uses_exist_in_sel |= in_selection; + uses_exist_out_sel |= !in_selection; + + if uses_exist_in_sel && uses_exist_out_sel { + break 'outside; + } } } - let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod( - def, - ctx, - curr_parent_module, - selection_range, - curr_file_id, - ); + let (def_in_mod, def_out_sel) = + check_def_in_mod_and_out_sel(def, ctx, curr_parent_module, selection_range, file_id); - let use_stmt_opt: Option = usage_res.into_iter().find_map(|(file_id, refs)| { - if file_id == curr_file_id { - refs.into_iter() - .rev() - .find_map(|fref| find_node_at_range(file.syntax(), fref.range)) - } else { - None - } + // Find use stmt that use def in current file + let use_stmt: Option = usage_res + .into_iter() + .filter(|(use_file_id, _)| *use_file_id == file_id) + .flat_map(|(_, refs)| refs.into_iter().rev()) + .find_map(|fref| find_node_at_range(file.syntax(), fref.range)); + let use_stmt_not_in_sel = use_stmt.as_ref().is_some_and(|use_stmt| { + !selection_range.contains_range(use_stmt.syntax().text_range()) }); - let mut use_tree_str_opt: Option> = None; + let mut use_tree_paths: Option> = None; //Exists inside and outside selection // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new // module @@ -534,37 +504,37 @@ impl Module { //get the use_tree_str, reconstruct the use stmt in new module let mut import_path_to_be_removed: Option = None; - if exists_inside_sel && exists_outside_sel { + if uses_exist_in_sel && uses_exist_out_sel { //Changes to be made only inside new module //If use_stmt exists, find the use_tree_str, reconstruct it inside new module //If not, insert a use stmt with super and the given nameref - if let Some((use_tree_str, _)) = - self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax) - { - use_tree_str_opt = Some(use_tree_str); - } else if source_exists_outside_sel_in_same_mod { - //Considered only after use_stmt is not present - //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel = - //true for all cases) - // false | false -> Do nothing - // false | true -> If source is in selection -> nothing to do, If source is outside - // mod -> ust_stmt transversal - // true | false -> super import insertion - // true | true -> super import insertion - self.make_use_stmt_of_node_with_super(node_syntax); + match self.process_use_stmt_for_import_resolve(use_stmt, use_node) { + Some((use_tree_str, _)) => use_tree_paths = Some(use_tree_str), + None if def_in_mod && def_out_sel => { + //Considered only after use_stmt is not present + //def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel = + //true for all cases) + // false | false -> Do nothing + // false | true -> If source is in selection -> nothing to do, If source is outside + // mod -> ust_stmt transversal + // true | false -> super import insertion + // true | true -> super import insertion + self.make_use_stmt_of_node_with_super(use_node); + } + None => {} } - } else if exists_inside_sel && !exists_outside_sel { + } else if uses_exist_in_sel && !uses_exist_out_sel { //Changes to be made inside new module, and remove import from outside if let Some((mut use_tree_str, text_range_opt)) = - self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax) + self.process_use_stmt_for_import_resolve(use_stmt, use_node) { if let Some(text_range) = text_range_opt { import_path_to_be_removed = Some(text_range); } - if source_exists_outside_sel_in_same_mod { + if def_in_mod && def_out_sel { if let Some(first_path_in_use_tree) = use_tree_str.last() { let first_path_in_use_tree_str = first_path_in_use_tree.to_string(); if !first_path_in_use_tree_str.contains("super") @@ -576,31 +546,43 @@ impl Module { } } - use_tree_str_opt = Some(use_tree_str); - } else if source_exists_outside_sel_in_same_mod { - self.make_use_stmt_of_node_with_super(node_syntax); + use_tree_paths = Some(use_tree_str); + } else if def_in_mod && def_out_sel { + self.make_use_stmt_of_node_with_super(use_node); } } - if let Some(use_tree_str) = use_tree_str_opt { - let mut use_tree_str = use_tree_str; - use_tree_str.reverse(); + if let Some(mut use_tree_paths) = use_tree_paths { + use_tree_paths.reverse(); - if !(!exists_outside_sel && exists_inside_sel && source_exists_outside_sel_in_same_mod) - { - if let Some(first_path_in_use_tree) = use_tree_str.first() { - let first_path_in_use_tree_str = first_path_in_use_tree.to_string(); - if first_path_in_use_tree_str.contains("super") { - let super_path = make::ext::ident_path("super"); - use_tree_str.insert(0, super_path) + if uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel { + if let Some(first_path_in_use_tree) = use_tree_paths.first() { + if first_path_in_use_tree.to_string().contains("super") { + use_tree_paths.insert(0, make::ext::ident_path("super")); } } } - let use_ = - make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false)); - let item = ast::Item::from(use_); - self.use_items.insert(0, item); + let is_item = matches!( + def, + Definition::Macro(_) + | Definition::Module(_) + | Definition::Function(_) + | Definition::Adt(_) + | Definition::Const(_) + | Definition::Static(_) + | Definition::Trait(_) + | Definition::TraitAlias(_) + | Definition::TypeAlias(_) + ); + + if (def_out_sel || !is_item) && use_stmt_not_in_sel { + let use_ = make::use_( + None, + make::use_tree(make::join_paths(use_tree_paths), None, None, false), + ); + self.use_items.insert(0, ast::Item::from(use_)); + } } import_path_to_be_removed @@ -621,33 +603,26 @@ impl Module { fn process_use_stmt_for_import_resolve( &self, - use_stmt_opt: Option, + use_stmt: Option, node_syntax: &SyntaxNode, ) -> Option<(Vec, Option)> { - if let Some(use_stmt) = use_stmt_opt { - for desc in use_stmt.syntax().descendants() { - if let Some(path_seg) = ast::PathSegment::cast(desc) { - if path_seg.syntax().to_string() == node_syntax.to_string() { - let mut use_tree_str = vec![path_seg.parent_path()]; - get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str); - for ancs in path_seg.syntax().ancestors() { - //Here we are looking for use_tree with same string value as node - //passed above as the range_to_remove function looks for a comma and - //then includes it in the text range to remove it. But the comma only - //appears at the use_tree level - if let Some(use_tree) = ast::UseTree::cast(ancs) { - if use_tree.syntax().to_string() == node_syntax.to_string() { - return Some(( - use_tree_str, - Some(range_to_remove(use_tree.syntax())), - )); - } - } - } + let use_stmt = use_stmt?; + for path_seg in use_stmt.syntax().descendants().filter_map(ast::PathSegment::cast) { + if path_seg.syntax().to_string() == node_syntax.to_string() { + let mut use_tree_str = vec![path_seg.parent_path()]; + get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str); - return Some((use_tree_str, None)); + //Here we are looking for use_tree with same string value as node + //passed above as the range_to_remove function looks for a comma and + //then includes it in the text range to remove it. But the comma only + //appears at the use_tree level + for use_tree in path_seg.syntax().ancestors().filter_map(ast::UseTree::cast) { + if use_tree.syntax().to_string() == node_syntax.to_string() { + return Some((use_tree_str, Some(range_to_remove(use_tree.syntax())))); } } + + return Some((use_tree_str, None)); } } @@ -676,145 +651,58 @@ fn check_intersection_and_push( import_paths_to_be_removed.push(import_path); } -fn does_source_exists_outside_sel_in_same_mod( +fn check_def_in_mod_and_out_sel( def: Definition, ctx: &AssistContext<'_>, curr_parent_module: &Option, selection_range: TextRange, curr_file_id: FileId, -) -> bool { - let mut source_exists_outside_sel_in_same_mod = false; +) -> (bool, bool) { + macro_rules! check_item { + ($x:ident) => { + if let Some(source) = $x.source(ctx.db()) { + let have_same_parent = if let Some(ast_module) = &curr_parent_module { + ctx.sema.to_module_def(ast_module).is_some_and(|it| it == $x.module(ctx.db())) + } else { + source.file_id.original_file(ctx.db()) == curr_file_id + }; + + let in_sel = !selection_range.contains_range(source.value.syntax().text_range()); + return (have_same_parent, in_sel); + } + }; + } + match def { Definition::Module(x) => { let source = x.definition_source(ctx.db()); - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - if let Some(hir_module) = x.parent(ctx.db()) { - compare_hir_and_ast_module(ast_module, hir_module, ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id + let have_same_parent = match (&curr_parent_module, x.parent(ctx.db())) { + (Some(ast_module), Some(hir_module)) => { + ctx.sema.to_module_def(ast_module).is_some_and(|it| it == hir_module) } - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id + _ => source.file_id.original_file(ctx.db()) == curr_file_id, }; if have_same_parent { if let ModuleSource::Module(module_) = source.value { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(module_.syntax().text_range()); + let in_sel = !selection_range.contains_range(module_.syntax().text_range()); + return (have_same_parent, in_sel); } } - } - Definition::Function(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Adt(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Variant(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Const(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Static(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::Trait(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } - } - Definition::TypeAlias(x) => { - if let Some(source) = x.source(ctx.db()) { - let have_same_parent = if let Some(ast_module) = &curr_parent_module { - compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some() - } else { - let source_file_id = source.file_id.original_file(ctx.db()); - source_file_id == curr_file_id - }; - - if have_same_parent { - source_exists_outside_sel_in_same_mod = - !selection_range.contains_range(source.value.syntax().text_range()); - } - } + return (have_same_parent, false); } + Definition::Function(x) => check_item!(x), + Definition::Adt(x) => check_item!(x), + Definition::Variant(x) => check_item!(x), + Definition::Const(x) => check_item!(x), + Definition::Static(x) => check_item!(x), + Definition::Trait(x) => check_item!(x), + Definition::TypeAlias(x) => check_item!(x), _ => {} } - source_exists_outside_sel_in_same_mod + (false, false) } fn get_replacements_for_visibility_change( @@ -834,24 +722,30 @@ fn get_replacements_for_visibility_change( *item = item.clone_for_update(); } //Use stmts are ignored + macro_rules! push_to_replacement { + ($it:ident) => { + replacements.push(($it.visibility(), $it.syntax().clone())) + }; + } + match item { - ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Enum(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())), + ast::Item::Const(it) => push_to_replacement!(it), + ast::Item::Enum(it) => push_to_replacement!(it), + ast::Item::ExternCrate(it) => push_to_replacement!(it), + ast::Item::Fn(it) => push_to_replacement!(it), //Associated item's visibility should not be changed ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it.clone()), - ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())), + ast::Item::MacroDef(it) => push_to_replacement!(it), + ast::Item::Module(it) => push_to_replacement!(it), + ast::Item::Static(it) => push_to_replacement!(it), ast::Item::Struct(it) => { - replacements.push((it.visibility(), it.syntax().clone())); + push_to_replacement!(it); record_field_parents.push((it.visibility(), it.syntax().clone())); } - ast::Item::Trait(it) => replacements.push((it.visibility(), it.syntax().clone())), - ast::Item::TypeAlias(it) => replacements.push((it.visibility(), it.syntax().clone())), + ast::Item::Trait(it) => push_to_replacement!(it), + ast::Item::TypeAlias(it) => push_to_replacement!(it), ast::Item::Union(it) => { - replacements.push((it.visibility(), it.syntax().clone())); + push_to_replacement!(it); record_field_parents.push((it.visibility(), it.syntax().clone())); } _ => (), @@ -865,8 +759,11 @@ fn get_use_tree_paths_from_path( path: ast::Path, use_tree_str: &mut Vec, ) -> Option<&mut Vec> { - path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| { - if let Some(use_tree) = ast::UseTree::cast(x) { + path.syntax() + .ancestors() + .filter(|x| x.to_string() != path.to_string()) + .filter_map(ast::UseTree::cast) + .find_map(|use_tree| { if let Some(upper_tree_path) = use_tree.path() { if upper_tree_path.to_string() != path.to_string() { use_tree_str.push(upper_tree_path.clone()); @@ -874,9 +771,8 @@ fn get_use_tree_paths_from_path( return Some(use_tree); } } - } - None - })?; + None + })?; Some(use_tree_str) } @@ -890,20 +786,6 @@ fn add_change_vis(vis: Option, node_or_token_opt: Option, -) -> Option<()> { - let hir_mod_name = hir_module.name(ctx.db())?; - let ast_mod_name = ast_module.name()?; - if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() { - return None; - } - - Some(()) -} - fn indent_range_before_given_node(node: &SyntaxNode) -> Option { node.siblings_with_tokens(syntax::Direction::Prev) .find(|x| x.kind() == WHITESPACE) @@ -1799,6 +1681,54 @@ mod modname { pub(crate) condvar: B, } } +"#, + ); + } + + #[test] + fn test_remove_import_path_inside_selection() { + check_assist( + extract_module, + r#" +$0struct Point; +impl Point { + pub const fn direction(self, other: Self) -> Option { + Some(Vertical) + } +} + +pub enum Direction { + Horizontal, + Vertical, +} +use Direction::{Horizontal, Vertical};$0 + +fn main() { + let x = Vertical; +} +"#, + r#" +mod modname { + use Direction::{Horizontal, Vertical}; + + pub(crate) struct Point; + + impl Point { + pub const fn direction(self, other: Self) -> Option { + Some(Vertical) + } + } + + pub enum Direction { + Horizontal, + Vertical, + } +} +use modname::Direction::{Horizontal, Vertical}; + +fn main() { + let x = Vertical; +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index fe2f8ed6417..ff051fa870f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -198,7 +198,7 @@ fn get_adt_source( adt: &hir::Adt, fn_name: &str, ) -> Option<(Option, FileId)> { - let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db); + let range = adt.source(ctx.sema.db)?.syntax().original_file_range_rooted(ctx.sema.db); let file = ctx.sema.parse(range.file_id); let adt_source = ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 50ec4347dc2..a90fe83857e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -206,7 +206,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let fn_body = fn_source.value.body()?; let param_list = fn_source.value.param_list()?; - let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db); + let FileRange { file_id, range } = fn_source.syntax().original_file_range_rooted(ctx.sema.db); if file_id == ctx.file_id() && range.contains(ctx.offset()) { cov_mark::hit!(inline_call_recursive); return None; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index 35e6b97eb78..4005753773c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -1,7 +1,10 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) -use hir::Semantics; -use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase}; -use syntax::ast::{self, IsString}; +use hir::MacroFileIdExt; +use ide_db::syntax_helpers::node_ext::macro_call_for_string_token; +use syntax::{ + ast::{self, IsString}, + AstToken, +}; use crate::{ completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind, @@ -32,10 +35,24 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, ctx: &CompletionContext<'_>, + original: &ast::String, expanded: &ast::String, ) -> Option<()> { - guard_env_macro(expanded, &ctx.sema)?; - let range = expanded.text_range_between_quotes()?; + let is_in_env_expansion = ctx + .sema + .hir_file_for(&expanded.syntax().parent()?) + .macro_file() + .map_or(false, |it| it.is_env_or_option_env(ctx.sema.db)); + if !is_in_env_expansion { + let call = macro_call_for_string_token(expanded)?; + let makro = ctx.sema.resolve_macro_call(&call)?; + // We won't map into `option_env` as that generates `None` for non-existent env vars + // so fall back to this lookup + if !makro.is_env_or_option_env(ctx.sema.db) { + return None; + } + } + let range = original.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|&(var, detail)| { let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); @@ -46,18 +63,6 @@ pub(crate) fn complete_cargo_env_vars( Some(()) } -fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> { - let call = macro_call_for_string_token(string)?; - let name = call.path()?.segment()?.name_ref()?; - let makro = semantics.resolve_macro_call(&call)?; - let db = semantics.db; - - match name.text().as_str() { - "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), - _ => None, - } -} - #[cfg(test)] mod tests { use crate::tests::{check_edit, completion_list}; @@ -68,7 +73,7 @@ mod tests { &format!( r#" #[rustc_builtin_macro] - macro_rules! {macro_name} {{ + macro {macro_name} {{ ($var:literal) => {{ 0 }} }} @@ -80,7 +85,7 @@ mod tests { &format!( r#" #[rustc_builtin_macro] - macro_rules! {macro_name} {{ + macro {macro_name} {{ ($var:literal) => {{ 0 }} }} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 7394d63be58..79467841502 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -96,7 +96,7 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node(&item)?; + let item = ctx.sema.original_syntax_node_rooted(&item)?; // item -> ASSOC_ITEM_LIST -> IMPL let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; let replacement_range = { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index ecf5b29e2c0..c2faa2d939d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -2,7 +2,7 @@ use std::iter; -use hir::{HirFileIdExt, Module, ModuleSource}; +use hir::{HirFileIdExt, Module}; use ide_db::{ base_db::{SourceDatabaseExt, VfsPath}, FxHashSet, RootDatabase, SymbolKind, @@ -57,7 +57,7 @@ pub(crate) fn complete_mod( .collect::>(); let module_declaration_file = - current_module.declaration_source(ctx.db).map(|module_declaration_source_file| { + current_module.declaration_source_range(ctx.db).map(|module_declaration_source_file| { module_declaration_source_file.file_id.original_file(ctx.db) }); @@ -148,9 +148,7 @@ fn module_chain_to_containing_module_file( ) -> Vec { let mut path = iter::successors(Some(current_module), |current_module| current_module.parent(db)) - .take_while(|current_module| { - matches!(current_module.definition_source(db).value, ModuleSource::Module(_)) - }) + .take_while(|current_module| current_module.is_inline(db)) .collect::>(); path.reverse(); path diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 4bab2886851..357060817c7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -369,6 +369,7 @@ impl CompletionItemKind { SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", SymbolKind::Macro => "ma", + SymbolKind::ProcMacro => "pm", SymbolKind::Module => "md", SymbolKind::SelfParam => "sp", SymbolKind::SelfType => "sy", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 912f2fba2b3..d89cfc8b6cb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -207,7 +207,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); - completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded); + completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index 017635d88e7..ec05f6d13d1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -205,6 +205,7 @@ impl RootDatabase { // SourceDatabaseExt base_db::FileTextQuery + base_db::CompressedFileTextQuery base_db::FileSourceRootQuery base_db::SourceRootQuery base_db::SourceRootCratesQuery diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 33970de1e4b..c0f0faba35c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -407,7 +407,7 @@ impl NameClass { } pub fn classify(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_name").entered(); + let _p = tracing::span!(tracing::Level::INFO, "NameClass::classify").entered(); let parent = name.syntax().parent()?; @@ -499,7 +499,8 @@ impl NameClass { sema: &Semantics<'_, RootDatabase>, lifetime: &ast::Lifetime, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_lifetime", ?lifetime).entered(); + let _p = tracing::span!(tracing::Level::INFO, "NameClass::classify_lifetime", ?lifetime) + .entered(); let parent = lifetime.syntax().parent()?; if let Some(it) = ast::LifetimeParam::cast(parent.clone()) { @@ -590,7 +591,8 @@ impl NameRefClass { sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_name_ref", ?name_ref).entered(); + let _p = + tracing::span!(tracing::Level::INFO, "NameRefClass::classify", ?name_ref).entered(); let parent = name_ref.syntax().parent()?; @@ -689,7 +691,8 @@ impl NameRefClass { sema: &Semantics<'_, RootDatabase>, lifetime: &ast::Lifetime, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "classify_lifetime_ref", ?lifetime).entered(); + let _p = tracing::span!(tracing::Level::INFO, "NameRefClass::classify_lifetime", ?lifetime) + .entered(); let parent = lifetime.syntax().parent()?; match parent.kind() { SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 4ac8a7c4c4a..db44b1e7232 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -71,7 +71,7 @@ pub fn visit_file_defs( let mut defs: VecDeque<_> = module.declarations(db).into(); while let Some(def) = defs.pop_front() { if let ModuleDef::Module(submodule) = def { - if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value { + if submodule.is_inline(db) { defs.extend(submodule.declarations(db)); submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index be08b37bac3..0d5a93f7b8e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -51,6 +51,7 @@ use std::{fmt, mem::ManuallyDrop}; use base_db::{ salsa::{self, Durability}, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, + DEFAULT_FILE_TEXT_LRU_CAP, }; use hir::db::{DefDatabase, ExpandDatabase, HirDatabase}; use triomphe::Arc; @@ -157,6 +158,7 @@ impl RootDatabase { pub fn update_base_query_lru_capacities(&mut self, lru_capacity: Option) { let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP); + base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); // macro expansions are usually rather small, so we can afford to keep more of them alive hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); @@ -166,6 +168,7 @@ impl RootDatabase { pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { use hir::db as hir_db; + base_db::FileTextQuery.in_db_mut(self).set_lru_capacity(DEFAULT_FILE_TEXT_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity( lru_capacities .get(stringify!(ParseQuery)) @@ -199,7 +202,7 @@ impl RootDatabase { // base_db::ProcMacrosQuery // SourceDatabaseExt - // base_db::FileTextQuery + base_db::FileTextQuery // base_db::FileSourceRootQuery // base_db::SourceRootQuery base_db::SourceRootCratesQuery @@ -348,6 +351,7 @@ pub enum SymbolKind { LifetimeParam, Local, Macro, + ProcMacro, Module, SelfParam, SelfType, @@ -366,9 +370,8 @@ pub enum SymbolKind { impl From for SymbolKind { fn from(it: hir::MacroKind) -> Self { match it { - hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::ProcMacro => { - SymbolKind::Macro - } + hir::MacroKind::Declarative | hir::MacroKind::BuiltIn => SymbolKind::Macro, + hir::MacroKind::ProcMacro => SymbolKind::ProcMacro, hir::MacroKind::Derive => SymbolKind::Derive, hir::MacroKind::Attr => SymbolKind::Attribute, } @@ -381,6 +384,7 @@ impl From for SymbolKind { hir::ModuleDefId::ConstId(..) => SymbolKind::Const, hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant, hir::ModuleDefId::FunctionId(..) => SymbolKind::Function, + hir::ModuleDefId::MacroId(hir::MacroId::ProcMacroId(..)) => SymbolKind::ProcMacro, hir::ModuleDefId::MacroId(..) => SymbolKind::Macro, hir::ModuleDefId::ModuleId(..) => SymbolKind::Module, hir::ModuleDefId::StaticId(..) => SymbolKind::Static, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 006d8882c11..a3ecc103605 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -190,22 +190,15 @@ impl SearchScope { let mut entries = IntMap::default(); let (file_id, range) = { - let InFile { file_id, value } = module.definition_source(db); + let InFile { file_id, value } = module.definition_source_range(db); if let Some(InRealFile { file_id, value: call_source }) = file_id.original_call_node(db) { (file_id, Some(call_source.text_range())) } else { - ( - file_id.original_file(db), - match value { - ModuleSource::SourceFile(_) => None, - ModuleSource::Module(it) => Some(it.syntax().text_range()), - ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()), - }, - ) + (file_id.original_file(db), Some(value)) } }; - entries.insert(file_id, range); + entries.entry(file_id).or_insert(range); let mut to_visit: Vec<_> = module.children(db).collect(); while let Some(module) = to_visit.pop() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index db28928a24e..a0fad7c850c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -38,7 +38,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option, d: &hir::UnresolvedField) -> Option> { + let mut fixes = Vec::new(); if d.method_with_same_name_exists { - method_fix(ctx, &d.expr) - } else { - // FIXME: add quickfix - - None + fixes.extend(method_fix(ctx, &d.expr)); } + fixes.extend(field_fix(ctx, d)); + if fixes.is_empty() { + None + } else { + Some(fixes) + } +} + +// FIXME: Add Snippet Support +fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option { + // Get the FileRange of the invalid field access + let root = ctx.sema.db.parse_or_expand(d.expr.file_id); + let expr = d.expr.value.to_node(&root); + + let error_range = ctx.sema.original_range_opt(expr.syntax())?; + let field_name = d.name.as_str()?; + // Convert the receiver to an ADT + let adt = d.receiver.strip_references().as_adt()?; + let target_module = adt.module(ctx.sema.db); + + let suggested_type = + if let Some(new_field_type) = ctx.sema.type_of_expr(&expr).map(|v| v.adjusted()) { + let display = + new_field_type.display_source_code(ctx.sema.db, target_module.into(), false).ok(); + make::ty(display.as_deref().unwrap_or("()")) + } else { + make::ty("()") + }; + + if !is_editable_crate(target_module.krate(), ctx.sema.db) { + return None; + } + + match adt { + Adt::Struct(adt_struct) => { + add_field_to_struct_fix(ctx, adt_struct, field_name, suggested_type, error_range) + } + Adt::Union(adt_union) => { + add_variant_to_union(ctx, adt_union, field_name, suggested_type, error_range) + } + _ => None, + } +} + +fn add_variant_to_union( + ctx: &DiagnosticsContext<'_>, + adt_union: Union, + field_name: &str, + suggested_type: Type, + error_range: FileRange, +) -> Option { + let adt_source = adt_union.source(ctx.sema.db)?; + let adt_syntax = adt_source.syntax(); + let field_list = adt_source.value.record_field_list()?; + let range = adt_syntax.original_file_range_rooted(ctx.sema.db); + let field_name = make::name(field_name); + + let (offset, record_field) = + record_field_layout(None, field_name, suggested_type, field_list, adt_syntax.value)?; + + let mut src_change_builder = SourceChangeBuilder::new(range.file_id); + src_change_builder.insert(offset, record_field); + Some(Assist { + id: AssistId("add-variant-to-union", AssistKind::QuickFix), + label: Label::new("Add field to union".to_owned()), + group: None, + target: error_range.range, + source_change: Some(src_change_builder.finish()), + trigger_signature_help: false, + }) +} + +fn add_field_to_struct_fix( + ctx: &DiagnosticsContext<'_>, + adt_struct: Struct, + field_name: &str, + suggested_type: Type, + error_range: FileRange, +) -> Option { + let struct_source = adt_struct.source(ctx.sema.db)?; + let struct_syntax = struct_source.syntax(); + let struct_range = struct_syntax.original_file_range_rooted(ctx.sema.db); + let field_list = struct_source.value.field_list(); + match field_list { + Some(FieldList::RecordFieldList(field_list)) => { + // Get range of final field in the struct + let visibility = if error_range.file_id == struct_range.file_id { + None + } else { + Some(make::visibility_pub_crate()) + }; + let field_name = make::name(field_name); + + let (offset, record_field) = record_field_layout( + visibility, + field_name, + suggested_type, + field_list, + struct_syntax.value, + )?; + + let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id); + + // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 + src_change_builder.insert(offset, record_field); + Some(Assist { + id: AssistId("add-field-to-record-struct", AssistKind::QuickFix), + label: Label::new("Add field to Record Struct".to_owned()), + group: None, + target: error_range.range, + source_change: Some(src_change_builder.finish()), + trigger_signature_help: false, + }) + } + None => { + // Add a field list to the Unit Struct + let mut src_change_builder = SourceChangeBuilder::new(struct_range.file_id); + let field_name = make::name(field_name); + let visibility = if error_range.file_id == struct_range.file_id { + None + } else { + Some(make::visibility_pub_crate()) + }; + // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 + let indent = IndentLevel::from_node(struct_syntax.value) + 1; + + let field = make::record_field(visibility, field_name, suggested_type).indent(indent); + let record_field_list = make::record_field_list(iter::once(field)); + // A Unit Struct with no `;` is invalid syntax. We should not suggest this fix. + let semi_colon = + algo::skip_trivia_token(struct_syntax.value.last_token()?, Direction::Prev)?; + if semi_colon.kind() != SyntaxKind::SEMICOLON { + return None; + } + src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string()); + + Some(Assist { + id: AssistId("convert-unit-struct-to-record-struct", AssistKind::QuickFix), + label: Label::new("Convert Unit Struct to Record Struct and add field".to_owned()), + group: None, + target: error_range.range, + source_change: Some(src_change_builder.finish()), + trigger_signature_help: false, + }) + } + Some(FieldList::TupleFieldList(_tuple)) => { + // FIXME: Add support for Tuple Structs. Tuple Structs are not sent to this diagnostic + None + } + } +} + +/// Used to determine the layout of the record field in the struct. +fn record_field_layout( + visibility: Option, + name: Name, + suggested_type: Type, + field_list: ast::RecordFieldList, + struct_syntax: &SyntaxNode, +) -> Option<(TextSize, String)> { + let (offset, needs_comma, trailing_new_line, indent) = match field_list.fields().last() { + Some(record_field) => { + let syntax = algo::skip_trivia_token(field_list.r_curly_token()?, Direction::Prev)?; + + let last_field_syntax = record_field.syntax(); + let last_field_indent = IndentLevel::from_node(last_field_syntax); + ( + last_field_syntax.text_range().end(), + syntax.kind() != SyntaxKind::COMMA, + false, + last_field_indent, + ) + } + // Empty Struct. Add a field right before the closing brace + None => { + let indent = IndentLevel::from_node(struct_syntax) + 1; + let offset = field_list.r_curly_token()?.text_range().start(); + (offset, false, true, indent) + } + }; + let comma = if needs_comma { ",\n" } else { "" }; + let trailing_new_line = if trailing_new_line { "\n" } else { "" }; + let record_field = make::record_field(visibility, name, suggested_type); + + Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}"))) } // FIXME: We should fill out the call here, move the cursor and trigger signature help fn method_fix( ctx: &DiagnosticsContext<'_>, expr_ptr: &InFile>, -) -> Option> { +) -> Option { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; - Some(vec![Assist { + Some(Assist { id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix), label: Label::new("Use parentheses to call the method".to_owned()), group: None, @@ -73,13 +266,15 @@ fn method_fix( TextEdit::insert(range.end(), "()".to_owned()), )), trigger_signature_help: false, - }]) + }) } #[cfg(test)] mod tests { + use crate::{ tests::{ check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled, + check_fix, }, DiagnosticsConfig, }; @@ -168,4 +363,100 @@ fn foo() { config.disabled.insert("syntax-error".to_owned()); check_diagnostics_with_config(config, "fn foo() { (). }"); } + + #[test] + fn unresolved_field_fix_on_unit() { + check_fix( + r#" + struct Foo; + + fn foo() { + Foo.bar$0; + } + "#, + r#" + struct Foo{ bar: () } + + fn foo() { + Foo.bar; + } + "#, + ); + } + #[test] + fn unresolved_field_fix_on_empty() { + check_fix( + r#" + struct Foo{ + } + + fn foo() { + let foo = Foo{}; + foo.bar$0; + } + "#, + r#" + struct Foo{ + bar: () + } + + fn foo() { + let foo = Foo{}; + foo.bar; + } + "#, + ); + } + #[test] + fn unresolved_field_fix_on_struct() { + check_fix( + r#" + struct Foo{ + a: i32 + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar$0; + } + "#, + r#" + struct Foo{ + a: i32, + bar: () + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar; + } + "#, + ); + } + #[test] + fn unresolved_field_fix_on_union() { + check_fix( + r#" + union Foo{ + a: i32 + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar$0; + } + "#, + r#" + union Foo{ + a: i32, + bar: () + } + + fn foo() { + let foo = Foo{a: 0}; + foo.bar; + } + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index 28ccf474b40..a9e1d07d7c5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -1,3 +1,11 @@ +use ide_db::{ + assists::{Assist, AssistId, AssistKind}, + base_db::FileRange, + label::Label, + source_change::SourceChange, +}; +use text_edit::TextEdit; + use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unused-variables @@ -8,18 +16,38 @@ pub(crate) fn unused_variables( d: &hir::UnusedVariable, ) -> Diagnostic { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + let diagnostic_range = ctx.sema.diagnostics_display_range(ast); + let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string(); Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcLint("unused_variables"), "unused variable", ast, ) + .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) .experimental() } +fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option> { + if is_in_marco { + return None; + } + Some(vec![Assist { + id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix), + label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)), + group: None, + target: diagnostic_range.range, + source_change: Some(SourceChange::from_text_edit( + diagnostic_range.file_id, + TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)), + )), + trigger_signature_help: false, + }]) +} + #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::tests::{check_diagnostics, check_fix, check_no_fix}; #[test] fn unused_variables_simple() { @@ -29,23 +57,23 @@ mod tests { struct Foo { f1: i32, f2: i64 } fn f(kkk: i32) {} - //^^^ warn: unused variable + //^^^ 💡 warn: unused variable fn main() { let a = 2; - //^ warn: unused variable + //^ 💡 warn: unused variable let b = 5; // note: `unused variable` implies `unused mut`, so we should not emit both at the same time. let mut c = f(b); - //^^^^^ warn: unused variable + //^^^^^ 💡 warn: unused variable let (d, e) = (3, 5); - //^ warn: unused variable + //^ 💡 warn: unused variable let _ = e; let f1 = 2; let f2 = 5; let f = Foo { f1, f2 }; match f { Foo { f1, f2 } => { - //^^ warn: unused variable + //^^ 💡 warn: unused variable _ = f2; } } @@ -53,7 +81,7 @@ fn main() { if g {} let h: fn() -> i32 = || 2; let i = h(); - //^ warn: unused variable + //^ 💡 warn: unused variable } "#, ); @@ -67,11 +95,11 @@ struct S { } impl S { fn owned_self(self, u: i32) {} - //^ warn: unused variable + //^ 💡 warn: unused variable fn ref_self(&self, u: i32) {} - //^ warn: unused variable + //^ 💡 warn: unused variable fn ref_mut_self(&mut self, u: i32) {} - //^ warn: unused variable + //^ 💡 warn: unused variable fn owned_mut_self(mut self) {} //^^^^^^^^ 💡 warn: variable does not need to be mutable @@ -103,7 +131,78 @@ fn main() { #[deny(unused)] fn main2() { let x = 2; - //^ error: unused variable + //^ 💡 error: unused variable +} +"#, + ); + } + + #[test] + fn fix_unused_variable() { + check_fix( + r#" +fn main() { + let x$0 = 2; +} +"#, + r#" +fn main() { + let _x = 2; +} +"#, + ); + + check_fix( + r#" +fn main() { + let ($0d, _e) = (3, 5); +} +"#, + r#" +fn main() { + let (_d, _e) = (3, 5); +} +"#, + ); + + check_fix( + r#" +struct Foo { f1: i32, f2: i64 } +fn main() { + let f = Foo { f1: 0, f2: 0 }; + match f { + Foo { f1$0, f2 } => { + _ = f2; + } + } +} +"#, + r#" +struct Foo { f1: i32, f2: i64 } +fn main() { + let f = Foo { f1: 0, f2: 0 }; + match f { + Foo { _f1, f2 } => { + _ = f2; + } + } +} +"#, + ); + } + + #[test] + fn no_fix_for_marco() { + check_no_fix( + r#" +macro_rules! my_macro { + () => { + let x = 3; + }; +} + +fn main() { + $0my_macro!(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index fb98e956847..cfda1c692ae 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -125,9 +125,12 @@ impl<'db, 'sema> Matcher<'db, 'sema> { let match_state = Matcher { sema, restrict_range: *restrict_range, rule }; // First pass at matching, where we check that node types and idents match. match_state.attempt_match_node(&mut Phase::First, &rule.pattern.node, code)?; - match_state.validate_range(&sema.original_range(code))?; + let file_range = sema + .original_range_opt(code) + .ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?; + match_state.validate_range(&file_range)?; let mut the_match = Match { - range: sema.original_range(code), + range: file_range, matched_node: code.clone(), placeholder_values: FxHashMap::default(), ignored_comments: Vec::new(), @@ -175,7 +178,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> { self.check_constraint(constraint, code)?; } if let Phase::Second(matches_out) = phase { - let original_range = self.sema.original_range(code); + let original_range = self + .sema + .original_range_opt(code) + .ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?; // We validated the range for the node when we started the match, so the placeholder // probably can't fail range validation, but just to be safe... self.validate_range(&original_range)?; @@ -487,7 +493,13 @@ impl<'db, 'sema> Matcher<'db, 'sema> { match_out.placeholder_values.insert( placeholder.ident.clone(), PlaceholderMatch::from_range(FileRange { - file_id: self.sema.original_range(code).file_id, + file_id: self + .sema + .original_range_opt(code) + .ok_or(MatchFailed { + reason: Some("def site definition".to_owned()), + })? + .file_id, range: first_matched_token .text_range() .cover(last_matched_token.text_range()), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs index 8d2d796122a..55a49da2424 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -190,12 +190,9 @@ impl MatchFinder<'_> { // When matching within a macro expansion, we only want to allow matches of // nodes that originated entirely from within the token tree of the macro call. // i.e. we don't want to match something that came from the macro itself. - self.slow_scan_node( - &expanded, - rule, - &Some(self.sema.original_range(tt.syntax())), - matches_out, - ); + if let Some(range) = self.sema.original_range_opt(tt.syntax()) { + self.slow_scan_node(&expanded, rule, &Some(range), matches_out); + } } } } @@ -227,7 +224,7 @@ impl MatchFinder<'_> { // There is no range restriction. return true; } - let node_range = self.sema.original_range(code); + let Some(node_range) = self.sema.original_range_opt(code) else { return false }; for range in &self.restrict_ranges { if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) { return true; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 1bda15255dc..ddeeca5f7b3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1,10 +1,10 @@ -use std::mem::discriminant; +use std::{iter, mem::discriminant}; use crate::{ doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget, RangeInfo, TryToNav, }; -use hir::{AsAssocItem, AssocItem, DescendPreference, ModuleDef, Semantics}; +use hir::{AsAssocItem, AssocItem, DescendPreference, MacroFileIdExt, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, defs::{Definition, IdentClass}, @@ -74,11 +74,13 @@ pub(crate) fn goto_definition( .filter_map(|token| { let parent = token.parent()?; - if let Some(tt) = ast::TokenTree::cast(parent.clone()) { - if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) { + if let Some(token) = ast::String::cast(token.clone()) { + if let Some(x) = try_lookup_include_path(sema, token, file_id) { return Some(vec![x]); } + } + if ast::TokenTree::can_cast(parent.kind()) { if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) { return Some(vec![x]); } @@ -111,24 +113,17 @@ pub(crate) fn goto_definition( fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, - tt: ast::TokenTree, - token: SyntaxToken, + token: ast::String, file_id: FileId, ) -> Option { - let token = ast::String::cast(token)?; - let path = token.value()?.into_owned(); - let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; - if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { + let file = sema.hir_file_for(&token.syntax().parent()?).macro_file()?; + if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file()) + // Check that we are in the eager argument expansion of an include macro + .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none()) + { return None; } - - // Ignore non-built-in macros to account for shadowing - if let Some(it) = sema.resolve_macro_call(¯o_call) { - if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) { - return None; - } - } + let path = token.value()?; let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; let size = sema.db.file_text(file_id).len().try_into().ok()?; @@ -1531,6 +1526,26 @@ fn main() { ); } + #[test] + fn goto_include_has_eager_input() { + check( + r#" +//- /main.rs +#[rustc_builtin_macro] +macro_rules! include_str {} +#[rustc_builtin_macro] +macro_rules! concat {} + +fn main() { + let str = include_str!(concat!("foo", ".tx$0t")); +} +//- /foo.txt +// empty +//^file +"#, + ); + } + #[test] fn goto_doc_include_str() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index d1d039534d5..63777d49105 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -510,7 +510,7 @@ fn render_notable_trait_comment( let mut needs_impl_header = true; for (trait_, assoc_types) in notable_traits { desc.push_str(if mem::take(&mut needs_impl_header) { - " // Implements notable traits: " + "// Implements notable traits: " } else { ", " }); @@ -661,7 +661,7 @@ fn closure_ty( if let Some(layout) = render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) { - format_to!(markup, "{layout}"); + format_to!(markup, " {layout}"); } if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { push_new_def(hir::Trait::from(trait_).into()) @@ -730,7 +730,7 @@ fn render_memory_layout( let config = config?; let layout = layout().ok()?; - let mut label = String::from(" // "); + let mut label = String::from("// "); if let Some(render) = config.size { let size = match tag(&layout) { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index c3cd6513dc6..4451e31870f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -180,7 +180,7 @@ fn foo() { *local* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let local: i32 ``` "#]], @@ -471,7 +471,7 @@ fn main() { *iter* ```rust - // size = 8, align = 4 + // size = 8, align = 4 let mut iter: Iter>, impl Fn(&mut u32, &u32, &mut u32) -> Option, u32>> ``` "#]], @@ -713,7 +713,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } ``` ```rust - // size = 1, align = 1, offset = 6 + // size = 1, align = 1, offset = 6 field_a: u8 ``` "#]], @@ -739,7 +739,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub field_a: u32 ``` "#]], @@ -762,7 +762,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub field_a: u32 ``` "#]], @@ -787,7 +787,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub 0: u32 ``` "#]], @@ -808,7 +808,7 @@ fn foo(foo: Foo) { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 pub 0: u32 ``` "#]], @@ -819,7 +819,7 @@ fn foo(foo: Foo) { fn hover_tuple_struct() { check( r#" -struct Foo$0(pub u32) +struct Foo$0(pub u32) where u32: Copy; "#, expect![[r#" *Foo* @@ -829,8 +829,100 @@ struct Foo$0(pub u32) ``` ```rust - // size = 4, align = 4 - struct Foo(pub u32); + // size = 4, align = 4 + struct Foo(pub u32) + where + u32: Copy, + ``` + "#]], + ); +} + +#[test] +fn hover_record_struct() { + check( + r#" +struct Foo$0 { field: u32 } +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo { + field: u32, + } + ``` + "#]], + ); + check( + r#" +struct Foo$0 where u32: Copy { field: u32 } +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo + where + u32: Copy, + { + field: u32, + } + ``` + "#]], + ); +} + +#[test] +fn hover_unit_struct() { + check( + r#" +struct Foo$0 where u32: Copy; +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 0, align = 1 + struct Foo + where + u32: Copy, + ``` + "#]], + ); +} + +#[test] +fn hover_type_alias() { + check( + r#" +type Fo$0o: Trait = S where T: Trait; +"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + type Foo: Trait = S + where + T: Trait, ``` "#]], ); @@ -957,7 +1049,7 @@ fn main() { *zz* ```rust - // size = 8, align = 4 + // size = 8, align = 4 let zz: Test ``` "#]], @@ -1009,7 +1101,7 @@ fn main() { let b$0ar = Some(12); } *bar* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let bar: Option ``` "#]], @@ -1079,7 +1171,7 @@ fn hover_for_local_variable() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1094,7 +1186,7 @@ fn hover_for_local_variable_pat() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1109,7 +1201,7 @@ fn hover_local_var_edge() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1124,7 +1216,7 @@ fn hover_for_param_edge() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 foo: i32 ``` "#]], @@ -1169,7 +1261,7 @@ fn main() { let foo_$0test = Thing::new(); } *foo_test* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let foo_test: Thing ``` "#]], @@ -1374,7 +1466,7 @@ fn y() { *x* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let x: i32 ``` "#]], @@ -1505,7 +1597,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } *bar* ```rust - // size = 4, align = 4 + // size = 4, align = 4 bar: u32 ``` "#]], @@ -1524,7 +1616,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } *bar* ```rust - // size = 4, align = 4 + // size = 4, align = 4 bar: u32 ``` "#]], @@ -1760,7 +1852,7 @@ fn test_hover_function_pointer_show_identifiers() { ``` ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 type foo = fn(a: i32, b: i32) -> i32 ``` "#]], @@ -1779,7 +1871,7 @@ fn test_hover_function_pointer_no_identifier() { ``` ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 type foo = fn(i32, i32) -> i32 ``` "#]], @@ -1926,7 +2018,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct Bar ``` @@ -1963,7 +2055,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct Bar ``` @@ -1993,7 +2085,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct Bar ``` @@ -2022,7 +2114,7 @@ pub struct B$0ar ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct Bar ``` @@ -2050,7 +2142,7 @@ pub struct B$0ar ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct Bar ``` @@ -2140,7 +2232,7 @@ fn test_hover_layout_of_variant() { ``` ```rust - // size = 4, align = 2 + // size = 4, align = 2 Variant1(u8, u16) ``` "#]], @@ -2162,7 +2254,7 @@ fn test_hover_layout_of_enum() { ``` ```rust - // size = 16 (0x10), align = 8, niches = 254 + // size = 16 (0x10), align = 8, niches = 254 enum Foo { Variant1(u8, u16), Variant2(i32, u8, i64), @@ -2540,7 +2632,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg(u32);", + description: "struct Arg(u32)", }, }, HoverGotoTypeData { @@ -2599,7 +2691,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 7..10, name: "Arg", kind: Struct, - description: "struct Arg(u32);", + description: "struct Arg(u32)", }, }, HoverGotoTypeData { @@ -2648,7 +2740,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 7..8, name: "A", kind: Struct, - description: "struct A(u32);", + description: "struct A(u32)", }, }, HoverGotoTypeData { @@ -2661,7 +2753,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } focus_range: 22..23, name: "B", kind: Struct, - description: "struct B(u32);", + description: "struct B(u32)", }, }, HoverGotoTypeData { @@ -2675,7 +2767,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); } name: "C", kind: Struct, container_name: "M", - description: "pub struct C(u32);", + description: "pub struct C(u32)", }, }, ], @@ -3331,26 +3423,26 @@ struct Foo; impl Foo {} "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::Bar", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..11, - focus_range: 7..10, - name: "Bar", - kind: Struct, - description: "struct Bar", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Bar", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Bar", + kind: Struct, + description: "struct Bar", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3396,26 +3488,26 @@ impl Foo { } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::Foo", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..11, - focus_range: 7..10, - name: "Foo", - kind: Struct, - description: "struct Foo", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Foo", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Foo", + kind: Struct, + description: "struct Foo", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3466,7 +3558,7 @@ fn main() { *f* ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 let f: &i32 ``` --- @@ -3476,7 +3568,7 @@ fn main() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 f: i32 ``` "#]], @@ -3498,7 +3590,7 @@ struct S$0T(T); ``` ```rust - struct ST(T); + struct ST(T) ``` "#]], ); @@ -3519,7 +3611,7 @@ struct S$0T(T); ``` ```rust - struct ST(T); + struct ST(T) ``` "#]], ); @@ -3541,7 +3633,7 @@ struct S$0T(T); ``` ```rust - struct ST(T); + struct ST(T) ``` "#]], ); @@ -3561,7 +3653,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<1> ``` "#]], @@ -3582,7 +3674,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<0> ``` "#]], @@ -3603,7 +3695,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<-1> ``` "#]], @@ -3624,7 +3716,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const ``` "#]], @@ -3645,7 +3737,7 @@ fn main() { *value* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let value: Const<'🦀'> ``` "#]], @@ -3665,7 +3757,7 @@ impl Foo { *self* ```rust - // size = 8, align = 8, niches = 1 + // size = 8, align = 8, niches = 1 self: &Foo ``` "#]], @@ -3686,7 +3778,7 @@ impl Foo { *self* ```rust - // size = 0, align = 1 + // size = 0, align = 1 self: Arc ``` "#]], @@ -4072,7 +4164,7 @@ type Fo$0o2 = Foo<2>; ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 type Foo2 = Foo<2> ``` "#]], @@ -4115,7 +4207,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 A = 8 ``` @@ -4141,7 +4233,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 A = 12 (0xC) ``` @@ -4168,7 +4260,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 B = 2 ``` @@ -4195,7 +4287,7 @@ enum E { ``` ```rust - // size = 1, align = 1 + // size = 1, align = 1 B = 5 ``` @@ -5002,7 +5094,7 @@ fn foo(e: E) { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 A = 3 ``` @@ -5025,7 +5117,7 @@ fn main() { *tile4* ```rust - // size = 32 (0x20), align = 4 + // size = 32 (0x20), align = 4 let tile4: [u32; 8] ``` "#]], @@ -5262,7 +5354,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct TheItem ``` @@ -5411,7 +5503,7 @@ mod string { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 struct String ``` @@ -5931,26 +6023,26 @@ fn foo() { } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::Foo", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..11, - focus_range: 7..10, - name: "Foo", - kind: Struct, - description: "struct Foo", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::Foo", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..11, + focus_range: 7..10, + name: "Foo", + kind: Struct, + description: "struct Foo", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -6139,7 +6231,7 @@ foo_macro!( ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 pub struct Foo ``` @@ -6165,8 +6257,8 @@ pub struct Foo(i32); ``` ```rust - // size = 4, align = 4 - pub struct Foo(i32); + // size = 4, align = 4 + pub struct Foo(i32) ``` --- @@ -6191,7 +6283,7 @@ pub struct Foo(T); ``` ```rust - pub struct Foo(T); + pub struct Foo(T) ``` --- @@ -6290,7 +6382,7 @@ enum Enum { ``` ```rust - // size = 4, align = 4 + // size = 4, align = 4 RecordV { field: u32 } ``` "#]], @@ -6313,7 +6405,7 @@ enum Enum { ``` ```rust - // size = 4, align = 4 + // size = 4, align = 4 field: u32 ``` "#]], @@ -6961,7 +7053,7 @@ fn test() { ``` ```rust - // size = 4, align = 4, offset = 0 + // size = 4, align = 4, offset = 0 f: u32 ``` "#]], @@ -6981,7 +7073,7 @@ fn test() { *s* ```rust - // size = 0, align = 1 + // size = 0, align = 1 let s: S ``` "#]], @@ -7002,7 +7094,7 @@ fn test() { *foo* ```rust - // size = 4, align = 4 + // size = 4, align = 4 let foo: i32 ``` "#]], @@ -7023,7 +7115,7 @@ format_args!("{aaaaa$0}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7044,7 +7136,7 @@ format_args!("{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7065,7 +7157,7 @@ format_args!(r"{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7091,7 +7183,7 @@ foo!(r"{$0aaaaa}"); *aaaaa* ```rust - // size = 16 (0x10), align = 8, niches = 1 + // size = 16 (0x10), align = 8, niches = 1 let aaaaa: &str ``` "#]], @@ -7440,8 +7532,8 @@ fn main(notable$0: u32) {} *notable* ```rust - // Implements notable traits: Notable - // size = 4, align = 4 + // Implements notable traits: Notable + // size = 4, align = 4 notable: u32 ``` "#]], @@ -7472,8 +7564,8 @@ impl Iterator for S { ``` ```rust - // Implements notable traits: Notable, Future, Iterator - // size = 0, align = 1 + // Implements notable traits: Notable, Future, Iterator + // size = 0, align = 1 struct S ``` "#]], @@ -7532,7 +7624,7 @@ extern "C" { ``` ```rust - // size = 0, align = 1 + // size = 0, align = 1 type Ty ``` "#]], @@ -7560,7 +7652,7 @@ fn main() { "#, expect![[r#" ```rust - // Implements notable traits: Notable, Future, Iterator + // Implements notable traits: Notable, Future, Iterator S ```"#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 8d9ad5bda14..5ba4e514e1f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -74,6 +74,10 @@ pub(super) fn hints( Ok(s) => s.value.text_range(), Err(_) => continue, }, + MirSpan::SelfParam => match source_map.self_param_syntax() { + Some(s) => s.value.text_range(), + None => continue, + }, MirSpan::Unknown => continue, }; let binding = &hir.bindings[*binding]; diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 59a7df14fd5..6955e14a10a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -259,7 +259,7 @@ impl Analysis { false, CrateOrigin::Local { repo: None, name: None }, ); - change.change_file(file_id, Some(Arc::from(text))); + change.change_file(file_id, Some(text)); change.set_crate_graph(crate_graph); change.set_target_data_layouts(vec![Err("fixture has no layout".into())]); change.set_toolchains(vec![None]); diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 674ce6d52bf..2123c98605d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -176,14 +176,12 @@ impl NavigationTarget { impl TryToNav for FileSymbol { fn try_to_nav(&self, db: &RootDatabase) -> Option> { - let root = db.parse_or_expand(self.loc.hir_file_id); - self.loc.ptr.to_node(&root); Some( - orig_range_with_focus( + orig_range_with_focus_r( db, self.loc.hir_file_id, - &self.loc.ptr.to_node(&root), - Some(self.loc.name_ptr.to_node(&root)), + self.loc.ptr.text_range(), + Some(self.loc.name_ptr.text_range()), ) .map(|(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget { @@ -722,7 +720,21 @@ fn orig_range_with_focus( value: &SyntaxNode, name: Option, ) -> UpmappingResult<(FileRange, Option)> { - let Some(name) = name else { return orig_range(db, hir_file, value) }; + orig_range_with_focus_r( + db, + hir_file, + value.text_range(), + name.map(|it| it.syntax().text_range()), + ) +} + +fn orig_range_with_focus_r( + db: &RootDatabase, + hir_file: HirFileId, + value: TextRange, + name: Option, +) -> UpmappingResult<(FileRange, Option)> { + let Some(name) = name else { return orig_range_r(db, hir_file, value) }; let call_kind = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id).kind; @@ -733,9 +745,9 @@ fn orig_range_with_focus( .definition_range(db) }; - let value_range = InFile::new(hir_file, value).original_file_range_opt(db); + let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db); let ((call_site_range, call_site_focus), def_site) = - match InFile::new(hir_file, name.syntax()).original_file_range_opt(db) { + match InFile::new(hir_file, name).original_node_file_range_opt(db) { // call site name Some((focus_range, ctxt)) if ctxt.is_root() => { // Try to upmap the node as well, if it ends up in the def site, go back to the call site @@ -802,7 +814,7 @@ fn orig_range_with_focus( } } // lost name? can't happen for single tokens - None => return orig_range(db, hir_file, value), + None => return orig_range_r(db, hir_file, value), }; UpmappingResult { @@ -840,7 +852,18 @@ fn orig_range( value: &SyntaxNode, ) -> UpmappingResult<(FileRange, Option)> { UpmappingResult { - call_site: (InFile::new(hir_file, value).original_file_range(db), None), + call_site: (InFile::new(hir_file, value).original_file_range_rooted(db), None), + def_site: None, + } +} + +fn orig_range_r( + db: &RootDatabase, + hir_file: HirFileId, + value: TextRange, +) -> UpmappingResult<(FileRange, Option)> { + UpmappingResult { + call_site: (InFile::new(hir_file, value).original_node_file_range(db).0, None), def_site: None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index dcdc6118a34..fef2aba3c61 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1710,7 +1710,7 @@ use proc_macros::mirror; mirror$0! {} "#, expect![[r#" - mirror Macro FileId(1) 1..77 22..28 + mirror ProcMacro FileId(1) 1..77 22..28 FileId(0) 26..32 "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 5fe46444ff4..79324bf3877 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -138,7 +138,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { }) { if let Some(def) = def { let file_id = match def { - Definition::Module(it) => it.declaration_source(db).map(|src| src.file_id), + Definition::Module(it) => { + it.declaration_source_range(db).map(|src| src.file_id) + } Definition::Function(it) => it.source(db).map(|src| src.file_id), _ => None, }; @@ -269,15 +271,10 @@ fn find_related_tests_in_module( Some(it) => it, _ => return, }; - let mod_source = parent_module.definition_source(sema.db); - let range = match &mod_source.value { - hir::ModuleSource::Module(m) => m.syntax().text_range(), - hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(), - hir::ModuleSource::SourceFile(f) => f.syntax().text_range(), - }; + let mod_source = parent_module.definition_source_range(sema.db); let file_id = mod_source.file_id.original_file(sema.db); - let mod_scope = SearchScope::file_range(FileRange { file_id, range }); + let mod_scope = SearchScope::file_range(FileRange { file_id, range: mod_source.value }); let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() }; find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests) } @@ -405,14 +402,15 @@ fn runnable_mod_outline_definition( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - match def.definition_source(sema.db).value { - hir::ModuleSource::SourceFile(_) => Some(Runnable { + if def.as_source_file_id(sema.db).is_some() { + Some(Runnable { use_name_in_title: false, nav: def.to_nav(sema.db).call_site(), kind: RunnableKind::TestMod { path }, cfg, - }), - _ => None, + }) + } else { + None } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index d2bd3bab14e..6aaf8f8e779 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -248,6 +248,7 @@ fn traverse( // an attribute nested in a macro call will not emit `inside_attribute` let mut inside_attribute = false; let mut inside_macro_call = false; + let mut inside_proc_macro_call = false; // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. @@ -298,8 +299,9 @@ fn traverse( ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => { bindings_shadow_count.clear() } - ast::Item::MacroCall(_) => { + ast::Item::MacroCall(ref macro_call) => { inside_macro_call = true; + inside_proc_macro_call = sema.is_proc_macro_call(macro_call); } _ => (), } @@ -344,6 +346,7 @@ fn traverse( } Some(ast::Item::MacroCall(_)) => { inside_macro_call = false; + inside_proc_macro_call = false; } _ => (), } @@ -519,6 +522,9 @@ fn traverse( highlight |= HlMod::Attribute } if inside_macro_call && tt_level > 0 { + if inside_proc_macro_call { + highlight |= HlMod::ProcMacro + } highlight |= HlMod::Macro } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index bbc6b55a642..a6dca0541e5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -98,6 +98,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 6d4cdd0efe2..5163a0de417 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -75,6 +75,7 @@ pub enum HlMod { Library, /// Used to differentiate individual elements within macro calls. Macro, + ProcMacro, /// Mutable binding. Mutable, /// Used for public items. @@ -146,6 +147,7 @@ impl HlTag { SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", SymbolKind::Macro => "macro", + SymbolKind::ProcMacro => "proc_macro", SymbolKind::Module => "module", SymbolKind::SelfParam => "self_keyword", SymbolKind::SelfType => "self_type_keyword", @@ -219,6 +221,7 @@ impl HlMod { HlMod::IntraDocLink, HlMod::Library, HlMod::Macro, + HlMod::ProcMacro, HlMod::Mutable, HlMod::Public, HlMod::Reference, @@ -243,6 +246,7 @@ impl HlMod { HlMod::IntraDocLink => "intra_doc_link", HlMod::Library => "library", HlMod::Macro => "macro", + HlMod::ProcMacro => "proc_macro", HlMod::Mutable => "mutable", HlMod::Public => "public", HlMod::Reference => "reference", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 4dcbfe4eb62..6994cb3d5c5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index bf5505caf37..dc2d103b581 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index 977d18c6b73..093cc2358a6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 0d1b3c1f183..154b823fffb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index dd1528ed03f..58613bf1510 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index d5f92aa5d47..34274932afc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 88a008796b3..729e4791f55 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index bdeb09d2f83..066fcfb1dfe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index f9c33b8a601..58a147dd80a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 2043752bc74..22ae5c82a4b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index ec39998de26..af779996593 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 4063cf9f757..32ac6a94d86 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -29,6 +29,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .numeric_literal { color: #BFEBBF; } .bool_literal { color: #BFE6EB; } .macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } .derive { color: #94BFF3; font-style: italic; } .module { color: #AFD8AF; } .value_param { color: #DCDCCC; } @@ -45,12 +46,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
use proc_macros::{mirror, identity, DeriveIdentity};
 
-mirror! {
-    {
-        ,i32 :x pub
-        ,i32 :y pub
-    } Foo struct
-}
+mirror! {
+    {
+        ,i32 :x pub
+        ,i32 :y pub
+    } Foo struct
+}
 macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
@@ -93,7 +94,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 
-include!(concat!("foo/", "foo.rs"));
+include!(concat!("foo/", "foo.rs"));
 
 fn main() {
     format_args!("Hello, {}!", (92,).0);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
index 4dcf8e5f01f..ef8a48ca1c1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
index 084bbf2f742..a2ded15fd1b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
index 1af4bcfbd9d..d123ee04976 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
index 7ee7b338c19..4429e5d933a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 84a823363f6..b6458fa7ca0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index c72ea54e948..49b588baa58 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -29,6 +29,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .numeric_literal    { color: #BFEBBF; }
 .bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
 .derive             { color: #94BFF3; font-style: italic; }
 .module             { color: #AFD8AF; }
 .value_param        { color: #DCDCCC; }
diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs
index 2e741021ea8..ca471399703 100644
--- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs
@@ -11,7 +11,7 @@ use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav
 
 #[derive(Debug)]
 pub enum TestItemKind {
-    Crate,
+    Crate(CrateId),
     Module,
     Function,
 }
@@ -32,15 +32,17 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec {
     crate_graph
         .iter()
         .filter(|&id| crate_graph[id].origin.is_local())
-        .filter_map(|id| Some(crate_graph[id].display_name.as_ref()?.to_string()))
-        .map(|id| TestItem {
-            kind: TestItemKind::Crate,
-            label: id.clone(),
-            id,
-            parent: None,
-            file: None,
-            text_range: None,
-            runnable: None,
+        .filter_map(|id| {
+            let test_id = crate_graph[id].display_name.as_ref()?.to_string();
+            Some(TestItem {
+                kind: TestItemKind::Crate(id),
+                label: test_id.clone(),
+                id: test_id,
+                parent: None,
+                file: None,
+                text_range: None,
+                runnable: None,
+            })
         })
         .collect()
 }
@@ -118,12 +120,13 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V
     let Some(crate_test_id) = &crate_graph[crate_id].display_name else {
         return vec![];
     };
+    let kind = TestItemKind::Crate(crate_id);
     let crate_test_id = crate_test_id.to_string();
     let crate_id: Crate = crate_id.into();
     let module = crate_id.root_module();
     let mut r = vec![TestItem {
         id: crate_test_id.clone(),
-        kind: TestItemKind::Crate,
+        kind,
         label: crate_test_id.clone(),
         parent: None,
         file: None,
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index a1c089520da..fe680e47fef 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -361,8 +361,8 @@ fn load_crate_graph(
     let changes = vfs.take_changes();
     for file in changes {
         if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change {
-            if let Ok(text) = std::str::from_utf8(&v) {
-                analysis_change.change_file(file.file_id, Some(text.into()))
+            if let Ok(text) = String::from_utf8(v) {
+                analysis_change.change_file(file.file_id, Some(text))
             }
         }
     }
@@ -419,14 +419,10 @@ impl ProcMacroExpander for Expander {
 #[cfg(test)]
 mod tests {
     use ide_db::base_db::SourceDatabase;
+    use vfs::file_set::FileSetConfigBuilder;
 
     use super::*;
 
-    use ide_db::base_db::SourceRootId;
-    use vfs::{file_set::FileSetConfigBuilder, VfsPath};
-
-    use crate::SourceRootConfig;
-
     #[test]
     fn test_loading_rust_analyzer() {
         let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index d946ecc1caf..ac7f0711784 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -1,6 +1,7 @@
 //! This module add real world mbe example for benchmark tests
 
 use rustc_hash::FxHashMap;
+use span::Span;
 use syntax::{
     ast::{self, HasName},
     AstNode, SmolStr,
@@ -9,7 +10,7 @@ use test_utils::{bench, bench_fixture, skip_slow_tests};
 
 use crate::{
     parser::{MetaVarKind, Op, RepeatKind, Separator},
-    syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanData, DummyTestSpanMap, DUMMY,
+    syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanMap, DUMMY,
 };
 
 #[test]
@@ -50,14 +51,14 @@ fn benchmark_expand_macro_rules() {
     assert_eq!(hash, 69413);
 }
 
-fn macro_rules_fixtures() -> FxHashMap> {
+fn macro_rules_fixtures() -> FxHashMap> {
     macro_rules_fixtures_tt()
         .into_iter()
         .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true)))
         .collect()
 }
 
-fn macro_rules_fixtures_tt() -> FxHashMap> {
+fn macro_rules_fixtures_tt() -> FxHashMap> {
     let fixture = bench_fixture::numerous_macro_rules();
     let source_file = ast::SourceFile::parse(&fixture).ok().unwrap();
 
@@ -79,8 +80,8 @@ fn macro_rules_fixtures_tt() -> FxHashMap
 
 /// Generate random invocation fixtures from rules
 fn invocation_fixtures(
-    rules: &FxHashMap>,
-) -> Vec<(String, tt::Subtree)> {
+    rules: &FxHashMap>,
+) -> Vec<(String, tt::Subtree)> {
     let mut seed = 123456789;
     let mut res = Vec::new();
 
@@ -128,8 +129,8 @@ fn invocation_fixtures(
     return res;
 
     fn collect_from_op(
-        op: &Op,
-        token_trees: &mut Vec>,
+        op: &Op,
+        token_trees: &mut Vec>,
         seed: &mut usize,
     ) {
         return match op {
@@ -221,19 +222,19 @@ fn invocation_fixtures(
             *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
             *seed
         }
-        fn make_ident(ident: &str) -> tt::TokenTree {
+        fn make_ident(ident: &str) -> tt::TokenTree {
             tt::Leaf::Ident(tt::Ident { span: DUMMY, text: SmolStr::new(ident) }).into()
         }
-        fn make_punct(char: char) -> tt::TokenTree {
+        fn make_punct(char: char) -> tt::TokenTree {
             tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into()
         }
-        fn make_literal(lit: &str) -> tt::TokenTree {
+        fn make_literal(lit: &str) -> tt::TokenTree {
             tt::Leaf::Literal(tt::Literal { span: DUMMY, text: SmolStr::new(lit) }).into()
         }
         fn make_subtree(
             kind: tt::DelimiterKind,
-            token_trees: Option>>,
-        ) -> tt::TokenTree {
+            token_trees: Option>>,
+        ) -> tt::TokenTree {
             tt::Subtree {
                 delimiter: tt::Delimiter { open: DUMMY, close: DUMMY, kind },
                 token_trees: token_trees.map(Vec::into_boxed_slice).unwrap_or_default(),
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index 3c270e30a9b..57d6082dd70 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -23,8 +23,11 @@ pub trait SpanMapper {
     fn span_for(&self, range: TextRange) -> S;
 }
 
-impl SpanMapper for SpanMap {
-    fn span_for(&self, range: TextRange) -> S {
+impl SpanMapper> for SpanMap
+where
+    SpanData: Span,
+{
+    fn span_for(&self, range: TextRange) -> SpanData {
         self.span_at(range.start())
     }
 }
@@ -38,32 +41,30 @@ impl> SpanMapper for &SM {
 /// Dummy things for testing where spans don't matter.
 pub(crate) mod dummy_test_span_utils {
 
+    use span::{Span, SyntaxContextId};
+
     use super::*;
 
-    pub type DummyTestSpanData = span::SpanData;
-    pub const DUMMY: DummyTestSpanData = span::SpanData {
+    pub const DUMMY: Span = Span {
         range: TextRange::empty(TextSize::new(0)),
         anchor: span::SpanAnchor {
             file_id: span::FileId::BOGUS,
             ast_id: span::ROOT_ERASED_FILE_AST_ID,
         },
-        ctx: DummyTestSyntaxContext,
+        ctx: SyntaxContextId::ROOT,
     };
 
-    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
-    pub struct DummyTestSyntaxContext;
-
     pub struct DummyTestSpanMap;
 
-    impl SpanMapper> for DummyTestSpanMap {
-        fn span_for(&self, range: syntax::TextRange) -> span::SpanData {
-            span::SpanData {
+    impl SpanMapper for DummyTestSpanMap {
+        fn span_for(&self, range: syntax::TextRange) -> Span {
+            Span {
                 range,
                 anchor: span::SpanAnchor {
                     file_id: span::FileId::BOGUS,
                     ast_id: span::ROOT_ERASED_FILE_AST_ID,
                 },
-                ctx: DummyTestSyntaxContext,
+                ctx: SyntaxContextId::ROOT,
             }
         }
     }
@@ -92,7 +93,7 @@ pub fn syntax_node_to_token_tree_modified(
     node: &SyntaxNode,
     map: SpanMap,
     append: FxHashMap>>>,
-    remove: FxHashSet,
+    remove: FxHashSet,
     call_site: SpanData,
 ) -> tt::Subtree>
 where
@@ -121,7 +122,7 @@ where
 pub fn token_tree_to_syntax_node(
     tt: &tt::Subtree>,
     entry_point: parser::TopEntryPoint,
-) -> (Parse, SpanMap>)
+) -> (Parse, SpanMap)
 where
     SpanData: Span,
     Ctx: Copy,
@@ -629,7 +630,7 @@ struct Converter {
     /// Used to make the emitted text ranges in the spans relative to the span anchor.
     map: SpanMap,
     append: FxHashMap>>,
-    remove: FxHashSet,
+    remove: FxHashSet,
     call_site: S,
 }
 
@@ -638,7 +639,7 @@ impl Converter {
         node: &SyntaxNode,
         map: SpanMap,
         append: FxHashMap>>,
-        remove: FxHashSet,
+        remove: FxHashSet,
         call_site: S,
     ) -> Self {
         let mut this = Converter {
@@ -660,16 +661,25 @@ impl Converter {
     fn next_token(&mut self) -> Option {
         while let Some(ev) = self.preorder.next() {
             match ev {
-                WalkEvent::Enter(SyntaxElement::Token(t)) => return Some(t),
-                WalkEvent::Enter(SyntaxElement::Node(n)) if self.remove.contains(&n) => {
-                    self.preorder.skip_subtree();
-                    if let Some(mut v) = self.append.remove(&n.into()) {
-                        v.reverse();
-                        self.current_leaves.extend(v);
-                        return None;
+                WalkEvent::Enter(token) => {
+                    if self.remove.contains(&token) {
+                        match token {
+                            syntax::NodeOrToken::Token(_) => {
+                                continue;
+                            }
+                            node => {
+                                self.preorder.skip_subtree();
+                                if let Some(mut v) = self.append.remove(&node) {
+                                    v.reverse();
+                                    self.current_leaves.extend(v);
+                                    return None;
+                                }
+                            }
+                        }
+                    } else if let syntax::NodeOrToken::Token(token) = token {
+                        return Some(token);
                     }
                 }
-                WalkEvent::Enter(SyntaxElement::Node(_)) => (),
                 WalkEvent::Leave(ele) => {
                     if let Some(mut v) = self.append.remove(&ele) {
                         v.reverse();
@@ -824,7 +834,7 @@ where
     cursor: Cursor<'a, SpanData>,
     text_pos: TextSize,
     inner: SyntaxTreeBuilder,
-    token_map: SpanMap>,
+    token_map: SpanMap,
 }
 
 impl<'a, Ctx> TtTreeSink<'a, Ctx>
@@ -841,7 +851,7 @@ where
         }
     }
 
-    fn finish(mut self) -> (Parse, SpanMap>) {
+    fn finish(mut self) -> (Parse, SpanMap) {
         self.token_map.finish();
         (self.inner.finish(), self.token_map)
     }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
index 11d1a728799..a261b1d4319 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
@@ -1,4 +1,5 @@
 use rustc_hash::FxHashMap;
+use span::Span;
 use syntax::{ast, AstNode};
 use test_utils::extract_annotations;
 use tt::{
@@ -6,7 +7,7 @@ use tt::{
     Leaf, Punct, Spacing,
 };
 
-use crate::{syntax_node_to_token_tree, DummyTestSpanData, DummyTestSpanMap, DUMMY};
+use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
 
 fn check_punct_spacing(fixture: &str) {
     let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
@@ -28,7 +29,7 @@ fn check_punct_spacing(fixture: &str) {
     while !cursor.eof() {
         while let Some(token_tree) = cursor.token_tree() {
             if let TokenTreeRef::Leaf(
-                Leaf::Punct(Punct { spacing, span: DummyTestSpanData { range, .. }, .. }),
+                Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }),
                 _,
             ) = token_tree
             {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index 54a20357d26..11b008fc0b4 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -8,7 +8,12 @@ use expect_test::expect;
 
 #[test]
 fn test_derive_empty() {
-    assert_expand("DeriveEmpty", r#"struct S;"#, expect!["SUBTREE $$ 1 1"], expect!["SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"]);
+    assert_expand(
+        "DeriveEmpty",
+        r#"struct S;"#,
+        expect!["SUBTREE $$ 1 1"],
+        expect!["SUBTREE $$ 42:2@0..100#0 42:2@0..100#0"],
+    );
 }
 
 #[test]
@@ -21,15 +26,15 @@ fn test_derive_error() {
               IDENT   compile_error 1
               PUNCH   ! [alone] 1
               SUBTREE () 1 1
-                LITERAL "#[derive(DeriveError)] struct S ;" 1
+                LITERAL "#[derive(DeriveError)] struct S ;"1
               PUNCH   ; [alone] 1"##]],
         expect![[r##"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-                LITERAL "#[derive(DeriveError)] struct S ;" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   compile_error 42:2@0..100#0
+              PUNCH   ! [alone] 42:2@0..100#0
+              SUBTREE () 42:2@0..100#0 42:2@0..100#0
+                LITERAL "#[derive(DeriveError)] struct S ;"42:2@0..100#0
+              PUNCH   ; [alone] 42:2@0..100#0"##]],
     );
 }
 
@@ -42,20 +47,20 @@ fn test_fn_like_macro_noop() {
             SUBTREE $$ 1 1
               IDENT   ident 1
               PUNCH   , [alone] 1
-              LITERAL 0 1
+              LITERAL 01
               PUNCH   , [alone] 1
-              LITERAL 1 1
+              LITERAL 11
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 0 SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 8..9, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 1 SpanData { range: 10..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE [] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 14..15, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   ident 42:2@0..5#0
+              PUNCH   , [alone] 42:2@5..6#0
+              LITERAL 042:2@7..8#0
+              PUNCH   , [alone] 42:2@8..9#0
+              LITERAL 142:2@10..11#0
+              PUNCH   , [alone] 42:2@11..12#0
+              SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]],
     );
 }
 
@@ -70,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE [] SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   ident 42:2@0..5#0
+              PUNCH   , [alone] 42:2@5..6#0
+              SUBTREE [] 42:2@7..8#0 42:2@7..8#0"#]],
     );
 }
 
@@ -86,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
             SUBTREE $$ 1 1
               IDENT   r#async 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   r#async SpanData { range: 0..7, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   r#async 42:2@0..7#0"#]],
     );
 }
 
@@ -100,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
             SUBTREE $$ 1 1
               IDENT   r#joined 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   r#joined SpanData { range: 0..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   r#joined 42:2@0..11#0"#]],
     );
 }
 
@@ -116,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
               IDENT   resolved_at_def_site 1
               IDENT   start_span 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   set_def_site SpanData { range: 0..150, anchor: SpanAnchor(FileId(41), 1), ctx: SyntaxContextId(0) }
-              IDENT   resolved_at_def_site SpanData { range: 13..33, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   start_span SpanData { range: 34..34, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   set_def_site 41:1@0..150#0
+              IDENT   resolved_at_def_site 42:2@13..33#0
+              IDENT   start_span 42:2@34..34#0"#]],
     );
 }
 
@@ -130,22 +135,22 @@ fn test_fn_like_mk_literals() {
         r#""#,
         expect![[r#"
             SUBTREE $$ 1 1
-              LITERAL b"byte_string" 1
-              LITERAL 'c' 1
-              LITERAL "string" 1
-              LITERAL 3.14f64 1
-              LITERAL 3.14 1
-              LITERAL 123i64 1
-              LITERAL 123 1"#]],
+              LITERAL b"byte_string"1
+              LITERAL 'c'1
+              LITERAL "string"1
+              LITERAL 3.14f641
+              LITERAL 3.141
+              LITERAL 123i641
+              LITERAL 1231"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL b"byte_string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 'c' SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL "string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 3.14f64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 3.14 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 123i64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 123 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              LITERAL b"byte_string"42:2@0..100#0
+              LITERAL 'c'42:2@0..100#0
+              LITERAL "string"42:2@0..100#0
+              LITERAL 3.14f6442:2@0..100#0
+              LITERAL 3.1442:2@0..100#0
+              LITERAL 123i6442:2@0..100#0
+              LITERAL 12342:2@0..100#0"#]],
     );
 }
 
@@ -159,9 +164,9 @@ fn test_fn_like_mk_idents() {
               IDENT   standard 1
               IDENT   r#raw 1"#]],
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   standard SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   r#raw SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   standard 42:2@0..100#0
+              IDENT   r#raw 42:2@0..100#0"#]],
     );
 }
 
@@ -172,48 +177,48 @@ fn test_fn_like_macro_clone_literals() {
         r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###,
         expect![[r###"
             SUBTREE $$ 1 1
-              LITERAL 1u16 1
+              LITERAL 1u161
               PUNCH   , [alone] 1
-              LITERAL 2_u32 1
+              LITERAL 2_u321
               PUNCH   , [alone] 1
               PUNCH   - [alone] 1
-              LITERAL 4i64 1
+              LITERAL 4i641
               PUNCH   , [alone] 1
-              LITERAL 3.14f32 1
+              LITERAL 3.14f321
               PUNCH   , [alone] 1
-              LITERAL "hello bridge" 1
+              LITERAL "hello bridge"1
               PUNCH   , [alone] 1
-              LITERAL "suffixed"suffix 1
+              LITERAL "suffixed"suffix1
               PUNCH   , [alone] 1
-              LITERAL r##"raw"## 1
+              LITERAL r##"raw"##1
               PUNCH   , [alone] 1
-              LITERAL 'a' 1
+              LITERAL 'a'1
               PUNCH   , [alone] 1
-              LITERAL b'b' 1
+              LITERAL b'b'1
               PUNCH   , [alone] 1
-              LITERAL c"null" 1"###]],
+              LITERAL c"null"1"###]],
         expect![[r###"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 1u16 SpanData { range: 0..4, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 4..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 2_u32 SpanData { range: 6..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   - [alone] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 4i64 SpanData { range: 14..18, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 18..19, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 3.14f32 SpanData { range: 20..27, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 27..28, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL "hello bridge" SpanData { range: 29..43, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 43..44, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL "suffixed"suffix SpanData { range: 45..61, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 61..62, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL r##"raw"## SpanData { range: 63..73, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 73..74, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL 'a' SpanData { range: 75..78, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 78..79, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL b'b' SpanData { range: 80..84, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   , [alone] SpanData { range: 84..85, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              LITERAL c"null" SpanData { range: 86..93, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"###]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              LITERAL 1u1642:2@0..4#0
+              PUNCH   , [alone] 42:2@4..5#0
+              LITERAL 2_u3242:2@6..11#0
+              PUNCH   , [alone] 42:2@11..12#0
+              PUNCH   - [alone] 42:2@13..14#0
+              LITERAL 4i6442:2@14..18#0
+              PUNCH   , [alone] 42:2@18..19#0
+              LITERAL 3.14f3242:2@20..27#0
+              PUNCH   , [alone] 42:2@27..28#0
+              LITERAL "hello bridge"42:2@29..43#0
+              PUNCH   , [alone] 42:2@43..44#0
+              LITERAL "suffixed"suffix42:2@45..61#0
+              PUNCH   , [alone] 42:2@61..62#0
+              LITERAL r##"raw"##42:2@63..73#0
+              PUNCH   , [alone] 42:2@73..74#0
+              LITERAL 'a'42:2@75..78#0
+              PUNCH   , [alone] 42:2@78..79#0
+              LITERAL b'b'42:2@80..84#0
+              PUNCH   , [alone] 42:2@84..85#0
+              LITERAL c"null"42:2@86..93#0"###]],
     );
 }
 
@@ -231,15 +236,15 @@ fn test_attr_macro() {
               IDENT   compile_error 1
               PUNCH   ! [alone] 1
               SUBTREE () 1 1
-                LITERAL "#[attr_error(some arguments)] mod m {}" 1
+                LITERAL "#[attr_error(some arguments)] mod m {}"1
               PUNCH   ; [alone] 1"##]],
         expect![[r##"
-            SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              IDENT   compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-                LITERAL "#[attr_error(some arguments)] mod m {}" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
-              PUNCH   ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]],
+            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
+              IDENT   compile_error 42:2@0..100#0
+              PUNCH   ! [alone] 42:2@0..100#0
+              SUBTREE () 42:2@0..100#0 42:2@0..100#0
+                LITERAL "#[attr_error(some arguments)] mod m {}"42:2@0..100#0
+              PUNCH   ; [alone] 42:2@0..100#0"##]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index 9a1311d9550..6050bc9e36e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -91,7 +91,7 @@ fn assert_expand_impl(
     let res = expander
         .expand(macro_name, fixture.into_subtree(call_site), attr, def_site, call_site, mixed_site)
         .unwrap();
-    expect_s.assert_eq(&format!("{res:?}"));
+    expect_s.assert_eq(&format!("{res:#?}"));
 }
 
 pub(crate) fn list() -> Vec {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 766606be7be..e6984d6f41b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -42,6 +42,7 @@ triomphe.workspace = true
 nohash-hasher.workspace = true
 always-assert = "0.2.0"
 walkdir = "2.3.2"
+semver.workspace = true
 
 cfg.workspace = true
 flycheck.workspace = true
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 07e04a83661..e747ec87b1c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -16,6 +16,7 @@ use std::{env, fs, path::PathBuf, process::ExitCode, sync::Arc};
 use anyhow::Context;
 use lsp_server::Connection;
 use rust_analyzer::{cli::flags, config::Config, from_json};
+use semver::Version;
 use tracing_subscriber::fmt::writer::BoxMakeWriter;
 use vfs::AbsPathBuf;
 
@@ -193,10 +194,18 @@ fn run_server() -> anyhow::Result<()> {
         }
     };
 
-    let mut is_visual_studio_code = false;
+    let mut visual_studio_code_version = None;
     if let Some(client_info) = client_info {
-        tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
-        is_visual_studio_code = client_info.name.starts_with("Visual Studio Code");
+        tracing::info!(
+            "Client '{}' {}",
+            client_info.name,
+            client_info.version.as_deref().unwrap_or_default()
+        );
+        visual_studio_code_version = client_info
+            .name
+            .starts_with("Visual Studio Code")
+            .then(|| client_info.version.as_deref().map(Version::parse).and_then(Result::ok))
+            .flatten();
     }
 
     let workspace_roots = workspace_folders
@@ -210,7 +219,8 @@ fn run_server() -> anyhow::Result<()> {
         })
         .filter(|workspaces| !workspaces.is_empty())
         .unwrap_or_else(|| vec![root_path.clone()]);
-    let mut config = Config::new(root_path, capabilities, workspace_roots, is_visual_studio_code);
+    let mut config =
+        Config::new(root_path, capabilities, workspace_roots, visual_studio_code_version);
     if let Some(json) = initialization_options {
         if let Err(e) = config.update(json) {
             use lsp_types::{
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index ef184032bfb..5c474908e7a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1053,7 +1053,7 @@ fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id:
     };
     let root = db.parse_or_expand(src.file_id);
     let node = src.map(|e| e.to_node(&root).syntax().clone());
-    let original_range = node.as_ref().original_file_range(db);
+    let original_range = node.as_ref().original_file_range_rooted(db);
     let path = vfs.file_path(original_range.file_id);
     let line_index = db.line_index(original_range.file_id);
     let text_range = original_range.range;
@@ -1069,7 +1069,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa
     };
     let root = db.parse_or_expand(src.file_id);
     let node = src.map(|e| e.to_node(&root).syntax().clone());
-    let original_range = node.as_ref().original_file_range(db);
+    let original_range = node.as_ref().original_file_range_rooted(db);
     let path = vfs.file_path(original_range.file_id);
     let line_index = db.line_index(original_range.file_id);
     let text_range = original_range.range;
@@ -1088,7 +1088,7 @@ fn expr_syntax_range<'a>(
     if let Ok(src) = src {
         let root = db.parse_or_expand(src.file_id);
         let node = src.map(|e| e.to_node(&root).syntax().clone());
-        let original_range = node.as_ref().original_file_range(db);
+        let original_range = node.as_ref().original_file_range_rooted(db);
         let path = vfs.file_path(original_range.file_id);
         let line_index = db.line_index(original_range.file_id);
         let text_range = original_range.range;
@@ -1109,7 +1109,7 @@ fn pat_syntax_range<'a>(
     if let Ok(src) = src {
         let root = db.parse_or_expand(src.file_id);
         let node = src.map(|e| e.to_node(&root).syntax().clone());
-        let original_range = node.as_ref().original_file_range(db);
+        let original_range = node.as_ref().original_file_range_rooted(db);
         let path = vfs.file_path(original_range.file_id);
         let line_index = db.line_index(original_range.file_id);
         let text_range = original_range.range;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 7ad87ab97fc..84f2e600874 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -134,7 +134,7 @@ impl Tester {
         let should_have_no_error = text.contains("// check-pass")
             || text.contains("// build-pass")
             || text.contains("// run-pass");
-        change.change_file(self.root_file, Some(Arc::from(text)));
+        change.change_file(self.root_file, Some(text));
         self.host.apply_change(change);
         let diagnostic_config = DiagnosticsConfig::test_sample();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 8fd59d159c9..1061a433a58 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -32,8 +32,8 @@ impl flags::Scip {
         let mut config = crate::config::Config::new(
             root.clone(),
             lsp_types::ClientCapabilities::default(),
-            /* workspace_roots = */ vec![],
-            /* is_visual_studio_code = */ false,
+            vec![],
+            None,
         );
 
         if let Some(p) = self.config_path {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 9e81c8dd665..cbf15246590 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -31,6 +31,7 @@ use project_model::{
     CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
+use semver::Version;
 use serde::{de::DeserializeOwned, Deserialize};
 use stdx::format_to_acc;
 use vfs::{AbsPath, AbsPathBuf};
@@ -624,7 +625,7 @@ pub struct Config {
     data: ConfigData,
     detached_files: Vec,
     snippets: Vec,
-    is_visual_studio_code: bool,
+    visual_studio_code_version: Option,
 }
 
 type ParallelCachePrimingNumThreads = u8;
@@ -823,7 +824,7 @@ impl Config {
         root_path: AbsPathBuf,
         caps: ClientCapabilities,
         workspace_roots: Vec,
-        is_visual_studio_code: bool,
+        visual_studio_code_version: Option,
     ) -> Self {
         Config {
             caps,
@@ -833,7 +834,7 @@ impl Config {
             root_path,
             snippets: Default::default(),
             workspace_roots,
-            is_visual_studio_code,
+            visual_studio_code_version,
         }
     }
 
@@ -1778,10 +1779,10 @@ impl Config {
         self.data.typing_autoClosingAngleBrackets_enable
     }
 
-    // FIXME: VSCode seems to work wrong sometimes, see https://github.com/microsoft/vscode/issues/193124
-    // hence, distinguish it for now.
-    pub fn is_visual_studio_code(&self) -> bool {
-        self.is_visual_studio_code
+    // VSCode is our reference implementation, so we allow ourselves to work around issues by
+    // special casing certain versions
+    pub fn visual_studio_code_version(&self) -> Option<&Version> {
+        self.visual_studio_code_version.as_ref()
     }
 }
 // Deserialization definitions
@@ -2694,7 +2695,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2710,7 +2711,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2726,7 +2727,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2745,7 +2746,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2764,7 +2765,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
@@ -2783,7 +2784,7 @@ mod tests {
             AbsPathBuf::try_from(project_root()).unwrap(),
             Default::default(),
             vec![],
-            false,
+            None,
         );
         config
             .update(serde_json::json!({
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e900f2601d8..6e6cc53c251 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -542,7 +542,7 @@ mod tests {
                 workspace_root.to_path_buf(),
                 ClientCapabilities::default(),
                 Vec::new(),
-                false,
+                None,
             ),
         );
         let snap = state.snapshot();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 0e560e54eda..1b4c33d8586 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -330,7 +330,7 @@ impl GlobalState {
                         // FIXME: Consider doing normalization in the `vfs` instead? That allows
                         // getting rid of some locking
                         let (text, line_endings) = LineEndings::normalize(text);
-                        (Arc::from(text), line_endings)
+                        (text, line_endings)
                     })
                 } else {
                     None
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 3bba4847f92..1c5a862c703 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -20,7 +20,6 @@ use ide_db::{
 };
 use project_model::CargoConfig;
 use test_utils::project_root;
-use triomphe::Arc;
 use vfs::{AbsPathBuf, VfsPath};
 
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -57,8 +56,6 @@ fn integrated_highlighting_benchmark() {
         vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
     };
 
-    let _g = crate::tracing::hprof::init("*>150");
-
     {
         let _it = stdx::timeit("initial");
         let analysis = host.analysis();
@@ -68,13 +65,16 @@ fn integrated_highlighting_benchmark() {
     {
         let _it = stdx::timeit("change");
         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
-        text.push_str("\npub fn _dummy() {}\n");
+        text = text.replace(
+            "self.data.cargo_buildScripts_rebuildOnSave",
+            "self. data. cargo_buildScripts_rebuildOnSave",
+        );
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
     }
 
-    let _g = crate::tracing::hprof::init("*>50");
+    let _g = crate::tracing::hprof::init("*>20");
 
     {
         let _it = stdx::timeit("after change");
@@ -125,7 +125,7 @@ fn integrated_completion_benchmark() {
             patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
                 + "sel".len();
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
         completion_offset
     };
@@ -168,7 +168,7 @@ fn integrated_completion_benchmark() {
             patch(&mut text, "sel;\ndb.struct_data(self.id)", ";sel;\ndb.struct_data(self.id)")
                 + ";sel".len();
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
         completion_offset
     };
@@ -210,7 +210,7 @@ fn integrated_completion_benchmark() {
             patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
                 + "self.".len();
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
         completion_offset
     };
@@ -307,7 +307,7 @@ fn integrated_diagnostics_benchmark() {
         let mut text = host.analysis().file_text(file_id).unwrap().to_string();
         patch(&mut text, "db.struct_data(self.id)", "();\ndb.struct_data(self.id)");
         let mut change = ChangeWithProcMacros::new();
-        change.change_file(file_id, Some(Arc::from(text)));
+        change.change_file(file_id, Some(text));
         host.apply_change(change);
     };
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index 86ab652f8ef..710ce7f8acb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -234,6 +234,13 @@ impl Notification for EndRunTest {
     const METHOD: &'static str = "experimental/endRunTest";
 }
 
+pub enum AppendOutputToRunTest {}
+
+impl Notification for AppendOutputToRunTest {
+    type Params = String;
+    const METHOD: &'static str = "experimental/appendOutputToRunTest";
+}
+
 pub enum AbortRunTest {}
 
 impl Notification for AbortRunTest {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index dd7dcf52778..c5081c4bea0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -85,6 +85,7 @@ define_semantic_token_types![
         (LIFETIME, "lifetime"),
         (LOGICAL, "logical") => OPERATOR,
         (MACRO_BANG, "macroBang") => MACRO,
+        (PROC_MACRO, "procMacro") => MACRO,
         (PARENTHESIS, "parenthesis"),
         (PUNCTUATION, "punctuation"),
         (SELF_KEYWORD, "selfKeyword") => KEYWORD,
@@ -143,6 +144,7 @@ define_semantic_token_modifiers![
         (INTRA_DOC_LINK, "intraDocLink"),
         (LIBRARY, "library"),
         (MACRO_MODIFIER, "macro"),
+        (PROC_MACRO_MODIFIER, "proc_macro"),
         (MUTABLE, "mutable"),
         (PUBLIC, "public"),
         (REFERENCE, "reference"),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index e2b55f4a5c5..e77d0c13bf2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -15,6 +15,7 @@ use ide::{
 };
 use ide_db::rust_doc::format_docs;
 use itertools::Itertools;
+use semver::VersionReq;
 use serde_json::to_value;
 use vfs::AbsPath;
 
@@ -56,6 +57,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
         SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE,
         SymbolKind::Macro
+        | SymbolKind::ProcMacro
         | SymbolKind::BuiltinAttr
         | SymbolKind::Attribute
         | SymbolKind::Derive
@@ -138,6 +140,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
             SymbolKind::Local => lsp_types::CompletionItemKind::VARIABLE,
             SymbolKind::Macro => lsp_types::CompletionItemKind::FUNCTION,
+            SymbolKind::ProcMacro => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::Module => lsp_types::CompletionItemKind::MODULE,
             SymbolKind::SelfParam => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::SelfType => lsp_types::CompletionItemKind::TYPE_PARAMETER,
@@ -443,17 +446,24 @@ pub(crate) fn inlay_hint(
     file_id: FileId,
     inlay_hint: InlayHint,
 ) -> Cancellable {
-    let is_visual_studio_code = snap.config.is_visual_studio_code();
     let needs_resolve = inlay_hint.needs_resolve;
     let (label, tooltip, mut something_to_resolve) =
         inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?;
-    let text_edits =
-        if !is_visual_studio_code && needs_resolve && fields_to_resolve.resolve_text_edits {
-            something_to_resolve |= inlay_hint.text_edit.is_some();
-            None
-        } else {
-            inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
-        };
+
+    let text_edits = if snap
+        .config
+        .visual_studio_code_version()
+        // https://github.com/microsoft/vscode/issues/193124
+        .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version))
+        && needs_resolve
+        && fields_to_resolve.resolve_text_edits
+    {
+        something_to_resolve |= inlay_hint.text_edit.is_some();
+        None
+    } else {
+        inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
+    };
+
     let data = if needs_resolve && something_to_resolve {
         Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap())
     } else {
@@ -667,6 +677,7 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::Trait => semantic_tokens::INTERFACE,
             SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
             SymbolKind::Macro => semantic_tokens::MACRO,
+            SymbolKind::ProcMacro => semantic_tokens::PROC_MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
         },
@@ -720,6 +731,7 @@ fn semantic_token_type_and_modifiers(
             HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
             HlMod::Library => semantic_tokens::LIBRARY,
             HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
+            HlMod::ProcMacro => semantic_tokens::PROC_MACRO_MODIFIER,
             HlMod::Mutable => semantic_tokens::MUTABLE,
             HlMod::Public => semantic_tokens::PUBLIC,
             HlMod::Reference => semantic_tokens::REFERENCE,
@@ -1507,13 +1519,28 @@ pub(crate) fn test_item(
         id: test_item.id,
         label: test_item.label,
         kind: match test_item.kind {
-            ide::TestItemKind::Crate => lsp_ext::TestItemKind::Package,
+            ide::TestItemKind::Crate(id) => 'b: {
+                let Some((cargo_ws, target)) = snap.cargo_target_for_crate_root(id) else {
+                    break 'b lsp_ext::TestItemKind::Package;
+                };
+                let target = &cargo_ws[target];
+                match target.kind {
+                    project_model::TargetKind::Bin
+                    | project_model::TargetKind::Lib { .. }
+                    | project_model::TargetKind::Example
+                    | project_model::TargetKind::BuildScript
+                    | project_model::TargetKind::Other => lsp_ext::TestItemKind::Package,
+                    project_model::TargetKind::Test | project_model::TargetKind::Bench => {
+                        lsp_ext::TestItemKind::Test
+                    }
+                }
+            }
             ide::TestItemKind::Module => lsp_ext::TestItemKind::Module,
             ide::TestItemKind::Function => lsp_ext::TestItemKind::Test,
         },
         can_resolve_children: matches!(
             test_item.kind,
-            ide::TestItemKind::Crate | ide::TestItemKind::Module
+            ide::TestItemKind::Crate(_) | ide::TestItemKind::Module
         ),
         parent: test_item.parent,
         text_document: test_item
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index bca6db19dcf..ffe56e41435 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -488,20 +488,22 @@ impl GlobalState {
 
     fn update_diagnostics(&mut self) {
         let db = self.analysis_host.raw_database();
-        let subscriptions = self
-            .mem_docs
-            .iter()
-            .map(|path| self.vfs.read().0.file_id(path).unwrap())
-            .filter(|&file_id| {
-                let source_root = db.file_source_root(file_id);
-                // Only publish diagnostics for files in the workspace, not from crates.io deps
-                // or the sysroot.
-                // While theoretically these should never have errors, we have quite a few false
-                // positives particularly in the stdlib, and those diagnostics would stay around
-                // forever if we emitted them here.
-                !db.source_root(source_root).is_library
-            })
-            .collect::>();
+        let subscriptions = {
+            let vfs = &self.vfs.read().0;
+            self.mem_docs
+                .iter()
+                .map(|path| vfs.file_id(path).unwrap())
+                .filter(|&file_id| {
+                    let source_root = db.file_source_root(file_id);
+                    // Only publish diagnostics for files in the workspace, not from crates.io deps
+                    // or the sysroot.
+                    // While theoretically these should never have errors, we have quite a few false
+                    // positives particularly in the stdlib, and those diagnostics would stay around
+                    // forever if we emitted them here.
+                    !db.source_root(source_root).is_library
+                })
+                .collect::>()
+        };
         tracing::trace!("updating notifications for {:?}", subscriptions);
 
         // Diagnostics are triggered by the user typing
@@ -797,6 +799,9 @@ impl GlobalState {
                 self.send_notification::(());
                 self.test_run_session = None;
             }
+            flycheck::CargoTestMessage::Custom { text } => {
+                self.send_notification::(text);
+            }
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index dfd25abc70f..1d831b8b105 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -171,7 +171,7 @@ impl Project<'_> {
                 ..Default::default()
             },
             roots,
-            false,
+            None,
         );
         config.update(self.config).expect("invalid config");
         config.rediscover_workspaces();
diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
index 4f6d792201b..e4b0a26a6ff 100644
--- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
@@ -26,9 +26,19 @@ use salsa::{InternId, InternValue};
 use crate::MacroCallId;
 
 /// Interned [`SyntaxContextData`].
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SyntaxContextId(InternId);
 
+impl fmt::Debug for SyntaxContextId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if f.alternate() {
+            write!(f, "{}", self.0.as_u32())
+        } else {
+            f.debug_tuple("SyntaxContextId").field(&self.0).finish()
+        }
+    }
+}
+
 impl salsa::InternKey for SyntaxContextId {
     fn from_intern_id(v: salsa::InternId) -> Self {
         SyntaxContextId(v)
diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs
index 0fe3275863d..6b849ce3738 100644
--- a/src/tools/rust-analyzer/crates/span/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/span/src/lib.rs
@@ -44,7 +44,10 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
 
 pub type Span = SpanData;
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+/// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs
+/// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental
+/// friendly.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct SpanData {
     /// The text range of this span, relative to the anchor.
     /// We need the anchor for incrementality, as storing absolute ranges will require
@@ -56,9 +59,35 @@ pub struct SpanData {
     pub ctx: Ctx,
 }
 
+impl fmt::Debug for SpanData {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if f.alternate() {
+            fmt::Debug::fmt(&self.anchor.file_id.index(), f)?;
+            f.write_char(':')?;
+            fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+            f.write_char('@')?;
+            fmt::Debug::fmt(&self.range, f)?;
+            f.write_char('#')?;
+            self.ctx.fmt(f)
+        } else {
+            f.debug_struct("SpanData")
+                .field("range", &self.range)
+                .field("anchor", &self.anchor)
+                .field("ctx", &self.ctx)
+                .finish()
+        }
+    }
+}
+
+impl SpanData {
+    pub fn eq_ignoring_ctx(self, other: Self) -> bool {
+        self.anchor == other.anchor && self.range == other.range
+    }
+}
+
 impl Span {
     #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
-    pub const DUMMY: Self = SpanData {
+    pub const DUMMY: Self = Self {
         range: TextRange::empty(TextSize::new(0)),
         anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
         ctx: SyntaxContextId::ROOT,
diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs
index 9f8101c816e..1f396a1e97b 100644
--- a/src/tools/rust-analyzer/crates/span/src/map.rs
+++ b/src/tools/rust-analyzer/crates/span/src/map.rs
@@ -1,23 +1,26 @@
 //! A map that maps a span to every position in a file. Usually maps a span to some range of positions.
 //! Allows bidirectional lookup.
 
-use std::hash::Hash;
+use std::{fmt, hash::Hash};
 
 use stdx::{always, itertools::Itertools};
 use syntax::{TextRange, TextSize};
 use vfs::FileId;
 
-use crate::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
+use crate::{
+    ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID,
+};
 
 /// Maps absolute text ranges for the corresponding file to the relevant span data.
 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
 pub struct SpanMap {
-    spans: Vec<(TextSize, S)>,
-    // FIXME: Should be
-    // spans: Vec<(TextSize, crate::SyntaxContextId)>,
+    spans: Vec<(TextSize, SpanData)>,
 }
 
-impl SpanMap {
+impl SpanMap
+where
+    SpanData: Copy,
+{
     /// Creates a new empty [`SpanMap`].
     pub fn empty() -> Self {
         Self { spans: Vec::new() }
@@ -34,7 +37,7 @@ impl SpanMap {
     }
 
     /// Pushes a new span onto the [`SpanMap`].
-    pub fn push(&mut self, offset: TextSize, span: S) {
+    pub fn push(&mut self, offset: TextSize, span: SpanData) {
         if cfg!(debug_assertions) {
             if let Some(&(last_offset, _)) = self.spans.last() {
                 assert!(
@@ -49,13 +52,31 @@ impl SpanMap {
     /// Returns all [`TextRange`]s that correspond to the given span.
     ///
     /// Note this does a linear search through the entire backing vector.
-    pub fn ranges_with_span(&self, span: S) -> impl Iterator + '_
+    pub fn ranges_with_span_exact(&self, span: SpanData) -> impl Iterator + '_
     where
-        S: Eq,
+        S: Copy,
     {
-        // FIXME: This should ignore the syntax context!
         self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
-            if s != span {
+            if !s.eq_ignoring_ctx(span) {
+                return None;
+            }
+            let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
+            Some(TextRange::new(start, end))
+        })
+    }
+
+    /// Returns all [`TextRange`]s whose spans contain the given span.
+    ///
+    /// Note this does a linear search through the entire backing vector.
+    pub fn ranges_with_span(&self, span: SpanData) -> impl Iterator + '_
+    where
+        S: Copy,
+    {
+        self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
+            if s.anchor != span.anchor {
+                return None;
+            }
+            if !s.range.contains_range(span.range) {
                 return None;
             }
             let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
@@ -64,21 +85,21 @@ impl SpanMap {
     }
 
     /// Returns the span at the given position.
-    pub fn span_at(&self, offset: TextSize) -> S {
+    pub fn span_at(&self, offset: TextSize) -> SpanData {
         let entry = self.spans.partition_point(|&(it, _)| it <= offset);
         self.spans[entry].1
     }
 
     /// Returns the spans associated with the given range.
     /// In other words, this will return all spans that correspond to all offsets within the given range.
-    pub fn spans_for_range(&self, range: TextRange) -> impl Iterator + '_ {
+    pub fn spans_for_range(&self, range: TextRange) -> impl Iterator> + '_ {
         let (start, end) = (range.start(), range.end());
         let start_entry = self.spans.partition_point(|&(it, _)| it <= start);
         let end_entry = self.spans[start_entry..].partition_point(|&(it, _)| it <= end); // FIXME: this might be wrong?
         self.spans[start_entry..][..end_entry].iter().map(|&(_, s)| s)
     }
 
-    pub fn iter(&self) -> impl Iterator + '_ {
+    pub fn iter(&self) -> impl Iterator)> + '_ {
         self.spans.iter().copied()
     }
 }
@@ -92,6 +113,16 @@ pub struct RealSpanMap {
     end: TextSize,
 }
 
+impl fmt::Display for RealSpanMap {
+    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().into_u32())?;
+        }
+        Ok(())
+    }
+}
+
 impl RealSpanMap {
     /// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id).
     pub fn absolute(file_id: FileId) -> Self {
diff --git a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs
index 899cd8ac6bb..d47b3d1647e 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs
@@ -194,21 +194,6 @@ impl<'a, A: ?Sized + Downcast, V: IntoBox> VacantEntry<'a, A, V> {
 mod tests {
     use super::*;
 
-    #[derive(Clone, Debug, PartialEq)]
-    struct A(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct B(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct C(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct D(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct E(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct F(i32);
-    #[derive(Clone, Debug, PartialEq)]
-    struct J(i32);
-
     #[test]
     fn test_varieties() {
         fn assert_send() {}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 1bc1ef8434f..c3d6f50e6b0 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -139,6 +139,17 @@ impl From for ast::Item {
     }
 }
 
+impl From for ast::Item {
+    fn from(extern_item: ast::ExternItem) -> Self {
+        match extern_item {
+            ast::ExternItem::Static(it) => ast::Item::Static(it),
+            ast::ExternItem::Fn(it) => ast::Item::Fn(it),
+            ast::ExternItem::MacroCall(it) => ast::Item::MacroCall(it),
+            ast::ExternItem::TypeAlias(it) => ast::Item::TypeAlias(it),
+        }
+    }
+}
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum AttrKind {
     Inner,
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index a654366c62a..8cf65d11c6c 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -149,12 +149,12 @@ impl ChangeFixture {
         for entry in fixture {
             let text = if entry.text.contains(CURSOR_MARKER) {
                 if entry.text.contains(ESCAPED_CURSOR_MARKER) {
-                    entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER).into()
+                    entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
                 } else {
                     let (range_or_offset, text) = extract_range_or_offset(&entry.text);
                     assert!(file_position.is_none());
                     file_position = Some((file_id, range_or_offset));
-                    text.into()
+                    text
                 }
             } else {
                 entry.text.as_str().into()
@@ -251,7 +251,7 @@ impl ChangeFixture {
             fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned()));
             roots.push(SourceRoot::new_library(fs));
 
-            source_change.change_file(core_file, Some(mini_core.source_code().into()));
+            source_change.change_file(core_file, Some(mini_core.source_code()));
 
             let all_crates = crate_graph.crates_in_topological_order();
 
@@ -287,7 +287,7 @@ impl ChangeFixture {
             );
             roots.push(SourceRoot::new_library(fs));
 
-            source_change.change_file(proc_lib_file, Some(source.into()));
+            source_change.change_file(proc_lib_file, Some(source));
 
             let all_crates = crate_graph.crates_in_topological_order();
 
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index eec88f80688..28289a6431e 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -177,17 +177,19 @@ fn print_debug_subtree(
     let align = "  ".repeat(level);
 
     let Delimiter { kind, open, close } = &subtree.delimiter;
-    let aux = match kind {
-        DelimiterKind::Invisible => format!("$$ {:?} {:?}", open, close),
-        DelimiterKind::Parenthesis => format!("() {:?} {:?}", open, close),
-        DelimiterKind::Brace => format!("{{}} {:?} {:?}", open, close),
-        DelimiterKind::Bracket => format!("[] {:?} {:?}", open, close),
+    let delim = match kind {
+        DelimiterKind::Invisible => "$$",
+        DelimiterKind::Parenthesis => "()",
+        DelimiterKind::Brace => "{}",
+        DelimiterKind::Bracket => "[]",
     };
 
-    if subtree.token_trees.is_empty() {
-        write!(f, "{align}SUBTREE {aux}")?;
-    } else {
-        writeln!(f, "{align}SUBTREE {aux}")?;
+    write!(f, "{align}SUBTREE {delim} ",)?;
+    fmt::Debug::fmt(&open, f)?;
+    write!(f, " ")?;
+    fmt::Debug::fmt(&close, f)?;
+    if !subtree.token_trees.is_empty() {
+        writeln!(f)?;
         for (idx, child) in subtree.token_trees.iter().enumerate() {
             print_debug_token(f, child, level + 1)?;
             if idx != subtree.token_trees.len() - 1 {
@@ -208,16 +210,24 @@ fn print_debug_token(
 
     match tkn {
         TokenTree::Leaf(leaf) => match leaf {
-            Leaf::Literal(lit) => write!(f, "{}LITERAL {} {:?}", align, lit.text, lit.span)?,
-            Leaf::Punct(punct) => write!(
-                f,
-                "{}PUNCH   {} [{}] {:?}",
-                align,
-                punct.char,
-                if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
-                punct.span
-            )?,
-            Leaf::Ident(ident) => write!(f, "{}IDENT   {} {:?}", align, ident.text, ident.span)?,
+            Leaf::Literal(lit) => {
+                write!(f, "{}LITERAL {}", align, lit.text)?;
+                fmt::Debug::fmt(&lit.span, f)?;
+            }
+            Leaf::Punct(punct) => {
+                write!(
+                    f,
+                    "{}PUNCH   {} [{}] ",
+                    align,
+                    punct.char,
+                    if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
+                )?;
+                fmt::Debug::fmt(&punct.span, f)?;
+            }
+            Leaf::Ident(ident) => {
+                write!(f, "{}IDENT   {} ", align, ident.text)?;
+                fmt::Debug::fmt(&ident.span, f)?;
+            }
         },
         TokenTree::Subtree(subtree) => {
             print_debug_subtree(f, subtree, level)?;
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index af5b4e51ef3..cf9ad5fe04d 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
  $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
+   |
+LL |         K = { () }
+   |             ^^^^^^
+
+error: higher-ranked subtype error
+  --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:12:13
+   |
+LL |         K = { () }
+   |             ^^^^^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: implementation of `Project` is not general enough
+  --> $DIR/assoc-const-eq-bound-var-in-ty-not-wf.rs:9:4
+   |
+LL | fn take(
+   |    ^^^^ implementation of `Project` is not general enough
+   |
+   = note: `Project` would have to be implemented for the type `for<'a> fn(&'a str) -> &'a str`
+   = note: ...but `Project` is actually implemented for the type `fn(&'0 str) -> &'0 str`, for some specific lifetime `'0`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs
new file mode 100644
index 00000000000..7fc6d564ca4
--- /dev/null
+++ b/tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs
@@ -0,0 +1,22 @@
+// Check that we don't reject non-escaping late-bound vars in the type of assoc const bindings.
+// There's no reason why we should disallow them.
+//
+//@ check-pass
+
+#![feature(associated_const_equality)]
+
+trait Trait {
+    const K: T;
+}
+
+fn take(
+    _: impl Trait<
+         fn(&'a str) -> &'a str as Discard>::Out,
+        K = { () }
+    >,
+) {}
+
+trait Discard { type Out; }
+impl Discard for T { type Out = (); }
+
+fn main() {}
diff --git a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs
new file mode 100644
index 00000000000..6db1e85ccfa
--- /dev/null
+++ b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.rs
@@ -0,0 +1,15 @@
+// Detect and reject escaping late-bound generic params in
+// the type of assoc consts used in an equality bound.
+#![feature(associated_const_equality)]
+
+trait Trait<'a> {
+    const K: &'a ();
+}
+
+fn take(_: impl for<'r> Trait<'r, K = { &() }>) {}
+//~^ ERROR the type of the associated constant `K` cannot capture late-bound generic parameters
+//~| NOTE its type cannot capture the late-bound lifetime parameter `'r`
+//~| NOTE the late-bound lifetime parameter `'r` is defined here
+//~| NOTE `K` has type `&'r ()`
+
+fn main() {}
diff --git a/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr
new file mode 100644
index 00000000000..349fddcafe8
--- /dev/null
+++ b/tests/ui/associated-consts/assoc-const-eq-esc-bound-var-in-ty.stderr
@@ -0,0 +1,12 @@
+error: the type of the associated constant `K` cannot capture late-bound generic parameters
+  --> $DIR/assoc-const-eq-esc-bound-var-in-ty.rs:9:35
+   |
+LL | fn take(_: impl for<'r> Trait<'r, K = { &() }>) {}
+   |                     --            ^ its type cannot capture the late-bound lifetime parameter `'r`
+   |                     |
+   |                     the late-bound lifetime parameter `'r` is defined here
+   |
+   = note: `K` has type `&'r ()`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs
new file mode 100644
index 00000000000..aaf16181030
--- /dev/null
+++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs
@@ -0,0 +1,55 @@
+// Regression test for issue #108271.
+// Detect and reject generic params in the type of assoc consts used in an equality bound.
+#![feature(associated_const_equality)]
+
+trait Trait<'a, T: 'a, const N: usize> {
+    const K: &'a [T; N];
+}
+
+fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
+//~^ ERROR the type of the associated constant `K` must not depend on generic parameters
+//~| NOTE its type must not depend on the lifetime parameter `'r`
+//~| NOTE the lifetime parameter `'r` is defined here
+//~| NOTE `K` has type `&'r [A; Q]`
+//~| ERROR the type of the associated constant `K` must not depend on generic parameters
+//~| NOTE its type must not depend on the type parameter `A`
+//~| NOTE the type parameter `A` is defined here
+//~| NOTE `K` has type `&'r [A; Q]`
+//~| ERROR the type of the associated constant `K` must not depend on generic parameters
+//~| NOTE its type must not depend on the const parameter `Q`
+//~| NOTE the const parameter `Q` is defined here
+//~| NOTE `K` has type `&'r [A; Q]`
+
+trait Project {
+    const SELF: Self;
+}
+
+fn take1(_: impl Project) {}
+//~^ ERROR the type of the associated constant `SELF` must not depend on `impl Trait`
+//~| NOTE its type must not depend on `impl Trait`
+//~| NOTE the `impl Trait` is specified here
+
+fn take2>(_: P) {}
+//~^ ERROR the type of the associated constant `SELF` must not depend on generic parameters
+//~| NOTE its type must not depend on the type parameter `P`
+//~| NOTE the type parameter `P` is defined here
+//~| NOTE `SELF` has type `P`
+
+trait Iface<'r> {
+    //~^ NOTE the lifetime parameter `'r` is defined here
+    type Assoc: Trait<'r, Self, Q, K = { loop {} }>
+    //~^ ERROR the type of the associated constant `K` must not depend on generic parameters
+    //~| NOTE its type must not depend on the lifetime parameter `'r`
+    //~| NOTE `K` has type `&'r [Self; Q]`
+    //~| ERROR the type of the associated constant `K` must not depend on `Self`
+    //~| NOTE its type must not depend on `Self`
+    //~| NOTE `K` has type `&'r [Self; Q]`
+    //~| ERROR the type of the associated constant `K` must not depend on generic parameters
+    //~| NOTE its type must not depend on the const parameter `Q`
+    //~| NOTE the const parameter `Q` is defined here
+    //~| NOTE `K` has type `&'r [Self; Q]`
+    where
+        Self: Sized + 'r;
+}
+
+fn main() {}
diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr
new file mode 100644
index 00000000000..077ac6e7f93
--- /dev/null
+++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr
@@ -0,0 +1,76 @@
+error: the type of the associated constant `K` must not depend on generic parameters
+  --> $DIR/assoc-const-eq-param-in-ty.rs:9:61
+   |
+LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
+   |          -- the lifetime parameter `'r` is defined here     ^ its type must not depend on the lifetime parameter `'r`
+   |
+   = note: `K` has type `&'r [A; Q]`
+
+error: the type of the associated constant `K` must not depend on generic parameters
+  --> $DIR/assoc-const-eq-param-in-ty.rs:9:61
+   |
+LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
+   |              - the type parameter `A` is defined here       ^ its type must not depend on the type parameter `A`
+   |
+   = note: `K` has type `&'r [A; Q]`
+
+error: the type of the associated constant `K` must not depend on generic parameters
+  --> $DIR/assoc-const-eq-param-in-ty.rs:9:61
+   |
+LL | fn take0<'r, A: 'r, const Q: usize>(_: impl Trait<'r, A, Q, K = { loop {} }>) {}
+   |                           -                                 ^ its type must not depend on the const parameter `Q`
+   |                           |
+   |                           the const parameter `Q` is defined here
+   |
+   = note: `K` has type `&'r [A; Q]`
+
+error: the type of the associated constant `SELF` must not depend on `impl Trait`
+  --> $DIR/assoc-const-eq-param-in-ty.rs:27:26
+   |
+LL | fn take1(_: impl Project) {}
+   |             -------------^^^^------
+   |             |            |
+   |             |            its type must not depend on `impl Trait`
+   |             the `impl Trait` is specified here
+
+error: the type of the associated constant `SELF` must not depend on generic parameters
+  --> $DIR/assoc-const-eq-param-in-ty.rs:32:21
+   |
+LL | fn take2>(_: P) {}
+   |          -          ^^^^ its type must not depend on the type parameter `P`
+   |          |
+   |          the type parameter `P` is defined here
+   |
+   = note: `SELF` has type `P`
+
+error: the type of the associated constant `K` must not depend on generic parameters
+  --> $DIR/assoc-const-eq-param-in-ty.rs:40:52
+   |
+LL | trait Iface<'r> {
+   |             -- the lifetime parameter `'r` is defined here
+LL |
+LL |     type Assoc: Trait<'r, Self, Q, K = { loop {} }>
+   |                                                    ^ its type must not depend on the lifetime parameter `'r`
+   |
+   = note: `K` has type `&'r [Self; Q]`
+
+error: the type of the associated constant `K` must not depend on `Self`
+  --> $DIR/assoc-const-eq-param-in-ty.rs:40:52
+   |
+LL |     type Assoc: Trait<'r, Self, Q, K = { loop {} }>
+   |                                                    ^ its type must not depend on `Self`
+   |
+   = note: `K` has type `&'r [Self; Q]`
+
+error: the type of the associated constant `K` must not depend on generic parameters
+  --> $DIR/assoc-const-eq-param-in-ty.rs:40:52
+   |
+LL |     type Assoc: Trait<'r, Self, Q, K = { loop {} }>
+   |                      -                             ^ its type must not depend on the const parameter `Q`
+   |                      |
+   |                      the const parameter `Q` is defined here
+   |
+   = note: `K` has type `&'r [Self; Q]`
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/associated-consts/issue-93835.rs b/tests/ui/associated-consts/issue-93835.rs
index b2a437fcbfb..9cc33d53f9c 100644
--- a/tests/ui/associated-consts/issue-93835.rs
+++ b/tests/ui/associated-consts/issue-93835.rs
@@ -6,7 +6,6 @@ fn e() {
     //~| ERROR cannot find value
     //~| ERROR associated const equality
     //~| ERROR cannot find trait `p` in this scope
-    //~| ERROR associated type bounds
 }
 
 fn main() {}
diff --git a/tests/ui/associated-consts/issue-93835.stderr b/tests/ui/associated-consts/issue-93835.stderr
index d3ce46f6f03..dfe78b3d1f3 100644
--- a/tests/ui/associated-consts/issue-93835.stderr
+++ b/tests/ui/associated-consts/issue-93835.stderr
@@ -26,17 +26,7 @@ LL |     type_ascribe!(p, a>);
    = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0658]: associated type bounds are unstable
-  --> $DIR/issue-93835.rs:4:24
-   |
-LL |     type_ascribe!(p, a>);
-   |                        ^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0405, E0412, E0425, E0658.
 For more information about an error, try `rustc --explain E0405`.
diff --git a/tests/ui/associated-type-bounds/ambiguous-associated-type.rs b/tests/ui/associated-type-bounds/ambiguous-associated-type.rs
index 4e6d8b9dd0a..ff3c6f2a95d 100644
--- a/tests/ui/associated-type-bounds/ambiguous-associated-type.rs
+++ b/tests/ui/associated-type-bounds/ambiguous-associated-type.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 pub struct Flatten
 where
     I: Iterator,
diff --git a/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs b/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs
index 8a580e19186..ca4d1f6d920 100644
--- a/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs
+++ b/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.rs
@@ -8,8 +8,6 @@
 // Additionally, as reported in https://github.com/rust-lang/rust/issues/63594,
 // we check that the spans for the error message are sane here.
 
-#![feature(associated_type_bounds)]
-
 fn main() {}
 
 trait Bar {
diff --git a/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr b/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr
index ad540909411..b1575abe571 100644
--- a/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr
+++ b/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr
@@ -1,5 +1,5 @@
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/assoc-type-eq-with-dyn-atb-fail.rs:30:28
+  --> $DIR/assoc-type-eq-with-dyn-atb-fail.rs:28:28
    |
 LL |     type Out = Box>;
    |                            ^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs b/tests/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs
index edde549bb4b..30fa51bb175 100644
--- a/tests/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs
+++ b/tests/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 use std::fmt::Debug;
 use std::iter::Once;
 
diff --git a/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.rs b/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.rs
index 81c8fe829f9..4689888719f 100644
--- a/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.rs
+++ b/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 trait B {
     type AssocType;
 }
diff --git a/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.stderr b/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.stderr
index 7d9870c72d4..5d8108f28a0 100644
--- a/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.stderr
+++ b/tests/ui/associated-type-bounds/bad-universal-in-dyn-in-where-clause.stderr
@@ -1,5 +1,5 @@
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/bad-universal-in-dyn-in-where-clause.rs:9:19
+  --> $DIR/bad-universal-in-dyn-in-where-clause.rs:7:19
    |
 LL |     dyn for<'j> B:,
    |                   ^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.rs b/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.rs
index f465123f34c..014550823bd 100644
--- a/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.rs
+++ b/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 trait Trait {
     type Item;
 }
diff --git a/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.stderr b/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.stderr
index 8855bd9c312..a017a601a17 100644
--- a/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.stderr
+++ b/tests/ui/associated-type-bounds/bad-universal-in-impl-sig.stderr
@@ -1,5 +1,5 @@
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/bad-universal-in-impl-sig.rs:10:16
+  --> $DIR/bad-universal-in-impl-sig.rs:8:16
    |
 LL | impl dyn Trait {}
    |                ^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/bounds-on-assoc-in-trait.rs b/tests/ui/associated-type-bounds/bounds-on-assoc-in-trait.rs
index dad1f644284..f69c392a8c3 100644
--- a/tests/ui/associated-type-bounds/bounds-on-assoc-in-trait.rs
+++ b/tests/ui/associated-type-bounds/bounds-on-assoc-in-trait.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 use std::fmt::Debug;
 use std::iter::Empty;
 use std::ops::Range;
diff --git a/tests/ui/associated-type-bounds/consts.rs b/tests/ui/associated-type-bounds/consts.rs
index 8f90c36ed45..17de1ff5682 100644
--- a/tests/ui/associated-type-bounds/consts.rs
+++ b/tests/ui/associated-type-bounds/consts.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 pub fn accept(_: impl Trait) {}
 //~^ ERROR expected type, found constant
 
diff --git a/tests/ui/associated-type-bounds/consts.stderr b/tests/ui/associated-type-bounds/consts.stderr
index 7f9fe5e500a..ddb677ec74d 100644
--- a/tests/ui/associated-type-bounds/consts.stderr
+++ b/tests/ui/associated-type-bounds/consts.stderr
@@ -1,5 +1,5 @@
 error: expected type, found constant
-  --> $DIR/consts.rs:3:29
+  --> $DIR/consts.rs:1:29
    |
 LL | pub fn accept(_: impl Trait) {}
    |                             ^------ bounds are not allowed on associated constants
@@ -7,7 +7,7 @@ LL | pub fn accept(_: impl Trait) {}
    |                             unexpected constant
    |
 note: the associated constant is defined here
-  --> $DIR/consts.rs:7:5
+  --> $DIR/consts.rs:5:5
    |
 LL |     const K: i32;
    |     ^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/duplicate.rs b/tests/ui/associated-type-bounds/duplicate.rs
index 54c8cd3fde0..06a1993da72 100644
--- a/tests/ui/associated-type-bounds/duplicate.rs
+++ b/tests/ui/associated-type-bounds/duplicate.rs
@@ -1,4 +1,3 @@
-#![feature(associated_type_bounds)]
 #![feature(type_alias_impl_trait)]
 
 use std::iter;
diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr
index 6345ef4b798..2d298f0a013 100644
--- a/tests/ui/associated-type-bounds/duplicate.stderr
+++ b/tests/ui/associated-type-bounds/duplicate.stderr
@@ -1,5 +1,5 @@
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:7:36
+  --> $DIR/duplicate.rs:6:36
    |
 LL | struct SI1> {
    |                        ----------  ^^^^^^^^^^ re-bound here
@@ -7,7 +7,7 @@ LL | struct SI1> {
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:11:36
+  --> $DIR/duplicate.rs:10:36
    |
 LL | struct SI2> {
    |                        ----------  ^^^^^^^^^^ re-bound here
@@ -15,7 +15,7 @@ LL | struct SI2> {
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:15:39
+  --> $DIR/duplicate.rs:14:39
    |
 LL | struct SI3> {
    |                        -------------  ^^^^^^^^^^^^^ re-bound here
@@ -23,7 +23,7 @@ LL | struct SI3> {
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:21:29
+  --> $DIR/duplicate.rs:20:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -31,7 +31,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:28:29
+  --> $DIR/duplicate.rs:27:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -39,7 +39,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:35:32
+  --> $DIR/duplicate.rs:34:32
    |
 LL |     T: Iterator,
    |                 -------------  ^^^^^^^^^^^^^ re-bound here
@@ -47,7 +47,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:41:34
+  --> $DIR/duplicate.rs:40:34
    |
 LL | enum EI1> {
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -55,7 +55,7 @@ LL | enum EI1> {
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:45:34
+  --> $DIR/duplicate.rs:44:34
    |
 LL | enum EI2> {
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -63,7 +63,7 @@ LL | enum EI2> {
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:49:37
+  --> $DIR/duplicate.rs:48:37
    |
 LL | enum EI3> {
    |                      -------------  ^^^^^^^^^^^^^ re-bound here
@@ -71,7 +71,7 @@ LL | enum EI3> {
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:55:29
+  --> $DIR/duplicate.rs:54:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -79,7 +79,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:62:29
+  --> $DIR/duplicate.rs:61:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -87,7 +87,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:69:32
+  --> $DIR/duplicate.rs:68:32
    |
 LL |     T: Iterator,
    |                 -------------  ^^^^^^^^^^^^^ re-bound here
@@ -95,7 +95,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:75:35
+  --> $DIR/duplicate.rs:74:35
    |
 LL | union UI1> {
    |                       ----------  ^^^^^^^^^^ re-bound here
@@ -103,7 +103,7 @@ LL | union UI1> {
    |                       `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:79:35
+  --> $DIR/duplicate.rs:78:35
    |
 LL | union UI2> {
    |                       ----------  ^^^^^^^^^^ re-bound here
@@ -111,7 +111,7 @@ LL | union UI2> {
    |                       `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:83:38
+  --> $DIR/duplicate.rs:82:38
    |
 LL | union UI3> {
    |                       -------------  ^^^^^^^^^^^^^ re-bound here
@@ -119,7 +119,7 @@ LL | union UI3> {
    |                       `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:89:29
+  --> $DIR/duplicate.rs:88:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -127,7 +127,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:96:29
+  --> $DIR/duplicate.rs:95:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -135,7 +135,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:103:32
+  --> $DIR/duplicate.rs:102:32
    |
 LL |     T: Iterator,
    |                 -------------  ^^^^^^^^^^^^^ re-bound here
@@ -143,7 +143,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:109:32
+  --> $DIR/duplicate.rs:108:32
    |
 LL | fn FI1>() {}
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -151,7 +151,7 @@ LL | fn FI1>() {}
    |                    `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:111:32
+  --> $DIR/duplicate.rs:110:32
    |
 LL | fn FI2>() {}
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -159,7 +159,7 @@ LL | fn FI2>() {}
    |                    `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:113:35
+  --> $DIR/duplicate.rs:112:35
    |
 LL | fn FI3>() {}
    |                    -------------  ^^^^^^^^^^^^^ re-bound here
@@ -167,7 +167,7 @@ LL | fn FI3>() {}
    |                    `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:117:29
+  --> $DIR/duplicate.rs:116:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -175,7 +175,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:123:29
+  --> $DIR/duplicate.rs:122:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -183,7 +183,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:129:32
+  --> $DIR/duplicate.rs:128:32
    |
 LL |     T: Iterator,
    |                 -------------  ^^^^^^^^^^^^^ re-bound here
@@ -191,7 +191,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:134:42
+  --> $DIR/duplicate.rs:133:42
    |
 LL | fn FRPIT1() -> impl Iterator {
    |                              ----------  ^^^^^^^^^^ re-bound here
@@ -199,7 +199,7 @@ LL | fn FRPIT1() -> impl Iterator {
    |                              `Item` bound here first
 
 error[E0282]: type annotations needed
-  --> $DIR/duplicate.rs:136:5
+  --> $DIR/duplicate.rs:135:5
    |
 LL |     iter::empty()
    |     ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty`
@@ -210,7 +210,7 @@ LL |     iter::empty::()
    |                +++++
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:139:42
+  --> $DIR/duplicate.rs:138:42
    |
 LL | fn FRPIT2() -> impl Iterator {
    |                              ----------  ^^^^^^^^^^ re-bound here
@@ -218,7 +218,7 @@ LL | fn FRPIT2() -> impl Iterator {
    |                              `Item` bound here first
 
 error[E0282]: type annotations needed
-  --> $DIR/duplicate.rs:141:5
+  --> $DIR/duplicate.rs:140:5
    |
 LL |     iter::empty()
    |     ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty`
@@ -229,7 +229,7 @@ LL |     iter::empty::()
    |                +++++
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:144:45
+  --> $DIR/duplicate.rs:143:45
    |
 LL | fn FRPIT3() -> impl Iterator {
    |                              -------------  ^^^^^^^^^^^^^ re-bound here
@@ -237,7 +237,7 @@ LL | fn FRPIT3() -> impl Iterator {
    |                              `Item` bound here first
 
 error[E0282]: type annotations needed
-  --> $DIR/duplicate.rs:146:5
+  --> $DIR/duplicate.rs:145:5
    |
 LL |     iter::empty()
    |     ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty`
@@ -248,7 +248,7 @@ LL |     iter::empty::()
    |                +++++
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:149:40
+  --> $DIR/duplicate.rs:148:40
    |
 LL | fn FAPIT1(_: impl Iterator) {}
    |                            ----------  ^^^^^^^^^^ re-bound here
@@ -256,7 +256,7 @@ LL | fn FAPIT1(_: impl Iterator) {}
    |                            `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:151:40
+  --> $DIR/duplicate.rs:150:40
    |
 LL | fn FAPIT2(_: impl Iterator) {}
    |                            ----------  ^^^^^^^^^^ re-bound here
@@ -264,7 +264,7 @@ LL | fn FAPIT2(_: impl Iterator) {}
    |                            `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:153:43
+  --> $DIR/duplicate.rs:152:43
    |
 LL | fn FAPIT3(_: impl Iterator) {}
    |                            -------------  ^^^^^^^^^^^^^ re-bound here
@@ -272,7 +272,7 @@ LL | fn FAPIT3(_: impl Iterator) {}
    |                            `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:156:35
+  --> $DIR/duplicate.rs:155:35
    |
 LL | type TAI1> = T;
    |                       ----------  ^^^^^^^^^^ re-bound here
@@ -280,7 +280,7 @@ LL | type TAI1> = T;
    |                       `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:158:35
+  --> $DIR/duplicate.rs:157:35
    |
 LL | type TAI2> = T;
    |                       ----------  ^^^^^^^^^^ re-bound here
@@ -288,7 +288,7 @@ LL | type TAI2> = T;
    |                       `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:160:38
+  --> $DIR/duplicate.rs:159:38
    |
 LL | type TAI3> = T;
    |                       -------------  ^^^^^^^^^^^^^ re-bound here
@@ -296,7 +296,7 @@ LL | type TAI3> = T;
    |                       `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:164:29
+  --> $DIR/duplicate.rs:163:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -304,7 +304,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:169:29
+  --> $DIR/duplicate.rs:168:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -312,7 +312,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:174:32
+  --> $DIR/duplicate.rs:173:32
    |
 LL |     T: Iterator,
    |                 -------------  ^^^^^^^^^^^^^ re-bound here
@@ -320,7 +320,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:178:36
+  --> $DIR/duplicate.rs:177:36
    |
 LL | type ETAI1> = impl Copy;
    |                        ----------  ^^^^^^^^^^ re-bound here
@@ -328,7 +328,7 @@ LL | type ETAI1> = impl Copy;
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:180:36
+  --> $DIR/duplicate.rs:179:36
    |
 LL | type ETAI2> = impl Copy;
    |                        ----------  ^^^^^^^^^^ re-bound here
@@ -336,7 +336,7 @@ LL | type ETAI2> = impl Copy;
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:182:39
+  --> $DIR/duplicate.rs:181:39
    |
 LL | type ETAI3> = impl Copy;
    |                        -------------  ^^^^^^^^^^^^^ re-bound here
@@ -344,7 +344,7 @@ LL | type ETAI3> = impl Copy;
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:184:40
+  --> $DIR/duplicate.rs:183:40
    |
 LL | type ETAI4 = impl Iterator;
    |                            ----------  ^^^^^^^^^^ re-bound here
@@ -352,7 +352,7 @@ LL | type ETAI4 = impl Iterator;
    |                            `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:186:40
+  --> $DIR/duplicate.rs:185:40
    |
 LL | type ETAI5 = impl Iterator;
    |                            ----------  ^^^^^^^^^^ re-bound here
@@ -360,7 +360,7 @@ LL | type ETAI5 = impl Iterator;
    |                            `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:188:43
+  --> $DIR/duplicate.rs:187:43
    |
 LL | type ETAI6 = impl Iterator;
    |                            -------------  ^^^^^^^^^^^^^ re-bound here
@@ -368,7 +368,7 @@ LL | type ETAI6 = impl Iterator;
    |                            `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:191:36
+  --> $DIR/duplicate.rs:190:36
    |
 LL | trait TRI1> {}
    |                        ----------  ^^^^^^^^^^ re-bound here
@@ -376,7 +376,7 @@ LL | trait TRI1> {}
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:193:36
+  --> $DIR/duplicate.rs:192:36
    |
 LL | trait TRI2> {}
    |                        ----------  ^^^^^^^^^^ re-bound here
@@ -384,7 +384,7 @@ LL | trait TRI2> {}
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:195:39
+  --> $DIR/duplicate.rs:194:39
    |
 LL | trait TRI3> {}
    |                        -------------  ^^^^^^^^^^^^^ re-bound here
@@ -392,7 +392,7 @@ LL | trait TRI3> {}
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:197:34
+  --> $DIR/duplicate.rs:196:34
    |
 LL | trait TRS1: Iterator {}
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -400,7 +400,7 @@ LL | trait TRS1: Iterator {}
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:197:34
+  --> $DIR/duplicate.rs:196:34
    |
 LL | trait TRS1: Iterator {}
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -410,7 +410,7 @@ LL | trait TRS1: Iterator {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:197:34
+  --> $DIR/duplicate.rs:196:34
    |
 LL | trait TRS1: Iterator {}
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -420,7 +420,7 @@ LL | trait TRS1: Iterator {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:201:34
+  --> $DIR/duplicate.rs:200:34
    |
 LL | trait TRS2: Iterator {}
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -428,7 +428,7 @@ LL | trait TRS2: Iterator {}
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:201:34
+  --> $DIR/duplicate.rs:200:34
    |
 LL | trait TRS2: Iterator {}
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -438,7 +438,7 @@ LL | trait TRS2: Iterator {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:201:34
+  --> $DIR/duplicate.rs:200:34
    |
 LL | trait TRS2: Iterator {}
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -448,7 +448,7 @@ LL | trait TRS2: Iterator {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:205:37
+  --> $DIR/duplicate.rs:204:37
    |
 LL | trait TRS3: Iterator {}
    |                      -------------  ^^^^^^^^^^^^^ re-bound here
@@ -456,7 +456,7 @@ LL | trait TRS3: Iterator {}
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:205:37
+  --> $DIR/duplicate.rs:204:37
    |
 LL | trait TRS3: Iterator {}
    |                      -------------  ^^^^^^^^^^^^^ re-bound here
@@ -466,7 +466,7 @@ LL | trait TRS3: Iterator {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:205:37
+  --> $DIR/duplicate.rs:204:37
    |
 LL | trait TRS3: Iterator {}
    |                      -------------  ^^^^^^^^^^^^^ re-bound here
@@ -476,7 +476,7 @@ LL | trait TRS3: Iterator {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:211:29
+  --> $DIR/duplicate.rs:210:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -484,7 +484,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:217:29
+  --> $DIR/duplicate.rs:216:29
    |
 LL |     T: Iterator,
    |                 ----------  ^^^^^^^^^^ re-bound here
@@ -492,7 +492,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:223:32
+  --> $DIR/duplicate.rs:222:32
    |
 LL |     T: Iterator,
    |                 -------------  ^^^^^^^^^^^^^ re-bound here
@@ -500,7 +500,7 @@ LL |     T: Iterator,
    |                 `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:229:32
+  --> $DIR/duplicate.rs:228:32
    |
 LL |     Self: Iterator,
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -508,7 +508,7 @@ LL |     Self: Iterator,
    |                    `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:229:32
+  --> $DIR/duplicate.rs:228:32
    |
 LL |     Self: Iterator,
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -518,7 +518,7 @@ LL |     Self: Iterator,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:229:32
+  --> $DIR/duplicate.rs:228:32
    |
 LL |     Self: Iterator,
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -528,7 +528,7 @@ LL |     Self: Iterator,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:237:32
+  --> $DIR/duplicate.rs:236:32
    |
 LL |     Self: Iterator,
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -536,7 +536,7 @@ LL |     Self: Iterator,
    |                    `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:237:32
+  --> $DIR/duplicate.rs:236:32
    |
 LL |     Self: Iterator,
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -546,7 +546,7 @@ LL |     Self: Iterator,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:237:32
+  --> $DIR/duplicate.rs:236:32
    |
 LL |     Self: Iterator,
    |                    ----------  ^^^^^^^^^^ re-bound here
@@ -556,7 +556,7 @@ LL |     Self: Iterator,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:245:35
+  --> $DIR/duplicate.rs:244:35
    |
 LL |     Self: Iterator,
    |                    -------------  ^^^^^^^^^^^^^ re-bound here
@@ -564,7 +564,7 @@ LL |     Self: Iterator,
    |                    `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:245:35
+  --> $DIR/duplicate.rs:244:35
    |
 LL |     Self: Iterator,
    |                    -------------  ^^^^^^^^^^^^^ re-bound here
@@ -574,7 +574,7 @@ LL |     Self: Iterator,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:245:35
+  --> $DIR/duplicate.rs:244:35
    |
 LL |     Self: Iterator,
    |                    -------------  ^^^^^^^^^^^^^ re-bound here
@@ -584,7 +584,7 @@ LL |     Self: Iterator,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:252:34
+  --> $DIR/duplicate.rs:251:34
    |
 LL |     type A: Iterator;
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -592,7 +592,7 @@ LL |     type A: Iterator;
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:256:34
+  --> $DIR/duplicate.rs:255:34
    |
 LL |     type A: Iterator;
    |                      ----------  ^^^^^^^^^^ re-bound here
@@ -600,7 +600,7 @@ LL |     type A: Iterator;
    |                      `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:260:37
+  --> $DIR/duplicate.rs:259:37
    |
 LL |     type A: Iterator;
    |                      -------------  ^^^^^^^^^^^^^ re-bound here
diff --git a/tests/ui/associated-type-bounds/elision.rs b/tests/ui/associated-type-bounds/elision.rs
index 5d7ed940ac6..8d35df1e4f4 100644
--- a/tests/ui/associated-type-bounds/elision.rs
+++ b/tests/ui/associated-type-bounds/elision.rs
@@ -1,4 +1,3 @@
-#![feature(associated_type_bounds)]
 #![feature(anonymous_lifetime_in_impl_trait)]
 
 // The same thing should happen for constraints in dyn trait.
diff --git a/tests/ui/associated-type-bounds/elision.stderr b/tests/ui/associated-type-bounds/elision.stderr
index 749dffdc4d3..36ca5a80024 100644
--- a/tests/ui/associated-type-bounds/elision.stderr
+++ b/tests/ui/associated-type-bounds/elision.stderr
@@ -1,5 +1,5 @@
 error[E0106]: missing lifetime specifier
-  --> $DIR/elision.rs:5:70
+  --> $DIR/elision.rs:4:70
    |
 LL | fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> { x.next() }
    |         ------------------------------------------------             ^^ expected named lifetime parameter
@@ -11,7 +11,7 @@ LL | fn f<'a>(x: &'a mut dyn Iterator>) -> Option<
    |     ++++     ++                                         ~~                  ~~
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/elision.rs:5:27
+  --> $DIR/elision.rs:4:27
    |
 LL | fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> { x.next() }
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/entails-sized-object-safety.rs b/tests/ui/associated-type-bounds/entails-sized-object-safety.rs
index 211c67061e6..ad2cbe48209 100644
--- a/tests/ui/associated-type-bounds/entails-sized-object-safety.rs
+++ b/tests/ui/associated-type-bounds/entails-sized-object-safety.rs
@@ -1,7 +1,5 @@
 //@ build-pass (FIXME(62277): could be check-pass?)
 
-#![feature(associated_type_bounds)]
-
 trait Tr1: Sized { type As1; }
 trait Tr2<'a>: Sized { type As2; }
 
diff --git a/tests/ui/associated-type-bounds/enum-bounds.rs b/tests/ui/associated-type-bounds/enum-bounds.rs
index b5087acb6b8..b1c42610862 100644
--- a/tests/ui/associated-type-bounds/enum-bounds.rs
+++ b/tests/ui/associated-type-bounds/enum-bounds.rs
@@ -1,6 +1,5 @@
 //@ run-pass
 
-#![feature(associated_type_bounds)]
 #![allow(dead_code)]
 
 trait Tr1 { type As1; }
diff --git a/tests/ui/associated-type-bounds/fn-apit.rs b/tests/ui/associated-type-bounds/fn-apit.rs
index 8e1897cc3d4..24bb5b93c99 100644
--- a/tests/ui/associated-type-bounds/fn-apit.rs
+++ b/tests/ui/associated-type-bounds/fn-apit.rs
@@ -2,8 +2,6 @@
 //@ aux-build:fn-aux.rs
 
 #![allow(unused)]
-#![feature(associated_type_bounds)]
-
 extern crate fn_aux;
 
 use fn_aux::*;
diff --git a/tests/ui/associated-type-bounds/fn-aux.rs b/tests/ui/associated-type-bounds/fn-aux.rs
index 6aaa0cc895f..26ba2cc4015 100644
--- a/tests/ui/associated-type-bounds/fn-aux.rs
+++ b/tests/ui/associated-type-bounds/fn-aux.rs
@@ -1,8 +1,6 @@
 //@ run-pass
 //@ aux-build:fn-aux.rs
 
-#![feature(associated_type_bounds)]
-
 extern crate fn_aux;
 
 use fn_aux::*;
diff --git a/tests/ui/associated-type-bounds/fn-inline.rs b/tests/ui/associated-type-bounds/fn-inline.rs
index 8435cb44a9a..971751cc115 100644
--- a/tests/ui/associated-type-bounds/fn-inline.rs
+++ b/tests/ui/associated-type-bounds/fn-inline.rs
@@ -2,8 +2,6 @@
 //@ aux-build:fn-aux.rs
 
 #![allow(unused)]
-#![feature(associated_type_bounds)]
-
 extern crate fn_aux;
 
 use fn_aux::*;
diff --git a/tests/ui/associated-type-bounds/fn-where.rs b/tests/ui/associated-type-bounds/fn-where.rs
index 3b6b557fb11..d28860bd4cc 100644
--- a/tests/ui/associated-type-bounds/fn-where.rs
+++ b/tests/ui/associated-type-bounds/fn-where.rs
@@ -2,8 +2,6 @@
 //@ aux-build:fn-aux.rs
 
 #![allow(unused)]
-#![feature(associated_type_bounds)]
-
 extern crate fn_aux;
 
 use fn_aux::*;
diff --git a/tests/ui/associated-type-bounds/fn-wrap-apit.rs b/tests/ui/associated-type-bounds/fn-wrap-apit.rs
index 4ce714d432f..7b55cd33b24 100644
--- a/tests/ui/associated-type-bounds/fn-wrap-apit.rs
+++ b/tests/ui/associated-type-bounds/fn-wrap-apit.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //@ aux-build:fn-aux.rs
 
-#![feature(associated_type_bounds)]
 #![allow(dead_code)]
 
 extern crate fn_aux;
diff --git a/tests/ui/associated-type-bounds/higher-ranked.rs b/tests/ui/associated-type-bounds/higher-ranked.rs
index 9e783c4bdca..a313b54f5b9 100644
--- a/tests/ui/associated-type-bounds/higher-ranked.rs
+++ b/tests/ui/associated-type-bounds/higher-ranked.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait A<'a> {
     type Assoc: ?Sized;
 }
diff --git a/tests/ui/associated-type-bounds/hrtb.rs b/tests/ui/associated-type-bounds/hrtb.rs
index 73c7a1a5677..1bf574f2e65 100644
--- a/tests/ui/associated-type-bounds/hrtb.rs
+++ b/tests/ui/associated-type-bounds/hrtb.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait A<'a> {}
 trait B<'b> {}
 fn foo()
diff --git a/tests/ui/associated-type-bounds/implied-bounds-cycle.rs b/tests/ui/associated-type-bounds/implied-bounds-cycle.rs
index 785d47d4791..8f2bec889ea 100644
--- a/tests/ui/associated-type-bounds/implied-bounds-cycle.rs
+++ b/tests/ui/associated-type-bounds/implied-bounds-cycle.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 trait A {
     type T;
 }
diff --git a/tests/ui/associated-type-bounds/implied-bounds-cycle.stderr b/tests/ui/associated-type-bounds/implied-bounds-cycle.stderr
index 1c1c64ea5f5..9ced7431210 100644
--- a/tests/ui/associated-type-bounds/implied-bounds-cycle.stderr
+++ b/tests/ui/associated-type-bounds/implied-bounds-cycle.stderr
@@ -1,12 +1,12 @@
 error[E0391]: cycle detected when computing the implied predicates of `B`
-  --> $DIR/implied-bounds-cycle.rs:7:15
+  --> $DIR/implied-bounds-cycle.rs:5:15
    |
 LL | trait B: A {}
    |               ^
    |
    = note: ...which immediately requires computing the implied predicates of `B` again
 note: cycle used when computing normalized predicates of `B`
-  --> $DIR/implied-bounds-cycle.rs:7:1
+  --> $DIR/implied-bounds-cycle.rs:5:1
    |
 LL | trait B: A {}
    | ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/implied-in-supertrait.rs b/tests/ui/associated-type-bounds/implied-in-supertrait.rs
index 83cb07d700a..639eb8dc6db 100644
--- a/tests/ui/associated-type-bounds/implied-in-supertrait.rs
+++ b/tests/ui/associated-type-bounds/implied-in-supertrait.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait Trait: Super {}
 
 trait Super {
diff --git a/tests/ui/associated-type-bounds/implied-region-constraints.rs b/tests/ui/associated-type-bounds/implied-region-constraints.rs
index 38219da61b4..ab03376c076 100644
--- a/tests/ui/associated-type-bounds/implied-region-constraints.rs
+++ b/tests/ui/associated-type-bounds/implied-region-constraints.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 trait Tr1 { type As1; }
 trait Tr2 { type As2; }
 
diff --git a/tests/ui/associated-type-bounds/implied-region-constraints.stderr b/tests/ui/associated-type-bounds/implied-region-constraints.stderr
index cddce8777ea..0aa76f732e4 100644
--- a/tests/ui/associated-type-bounds/implied-region-constraints.stderr
+++ b/tests/ui/associated-type-bounds/implied-region-constraints.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/implied-region-constraints.rs:17:56
+  --> $DIR/implied-region-constraints.rs:15:56
    |
 LL | fn _bad_st<'a, 'b, T>(x: St<'a, 'b, T>)
    |            --  -- lifetime `'b` defined here
@@ -12,7 +12,7 @@ LL |     let _failure_proves_not_implied_outlives_region_b: &'b T = &x.f0;
    = help: consider adding the following bound: `'a: 'b`
 
 error: lifetime may not live long enough
-  --> $DIR/implied-region-constraints.rs:38:64
+  --> $DIR/implied-region-constraints.rs:36:64
    |
 LL | fn _bad_en7<'a, 'b, T>(x: En7<'a, 'b, T>)
    |             --  -- lifetime `'b` defined here
diff --git a/tests/ui/associated-type-bounds/inside-adt.rs b/tests/ui/associated-type-bounds/inside-adt.rs
index 2b4b060983e..bf520d7ee38 100644
--- a/tests/ui/associated-type-bounds/inside-adt.rs
+++ b/tests/ui/associated-type-bounds/inside-adt.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 use std::mem::ManuallyDrop;
 
 struct S1 { f: dyn Iterator }
diff --git a/tests/ui/associated-type-bounds/inside-adt.stderr b/tests/ui/associated-type-bounds/inside-adt.stderr
index ef45fae8f2a..ff9e2585264 100644
--- a/tests/ui/associated-type-bounds/inside-adt.stderr
+++ b/tests/ui/associated-type-bounds/inside-adt.stderr
@@ -1,53 +1,53 @@
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:5:29
+  --> $DIR/inside-adt.rs:3:29
    |
 LL | struct S1 { f: dyn Iterator }
    |                             ^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:7:33
+  --> $DIR/inside-adt.rs:5:33
    |
 LL | struct S2 { f: Box> }
    |                                 ^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:9:29
+  --> $DIR/inside-adt.rs:7:29
    |
 LL | struct S3 { f: dyn Iterator }
    |                             ^^^^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:12:26
+  --> $DIR/inside-adt.rs:10:26
    |
 LL | enum E1 { V(dyn Iterator) }
    |                          ^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:14:30
+  --> $DIR/inside-adt.rs:12:30
    |
 LL | enum E2 { V(Box>) }
    |                              ^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:16:26
+  --> $DIR/inside-adt.rs:14:26
    |
 LL | enum E3 { V(dyn Iterator) }
    |                          ^^^^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:19:41
+  --> $DIR/inside-adt.rs:17:41
    |
 LL | union U1 { f: ManuallyDrop> }
    |                                         ^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:21:45
+  --> $DIR/inside-adt.rs:19:45
    |
 LL | union U2 { f: ManuallyDrop>> }
    |                                             ^^^^^^^^^^
 
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/inside-adt.rs:23:41
+  --> $DIR/inside-adt.rs:21:41
    |
 LL | union U3 { f: ManuallyDrop> }
    |                                         ^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/issue-104916.rs b/tests/ui/associated-type-bounds/issue-104916.rs
index ee29a0a2fc4..75f327e6ee7 100644
--- a/tests/ui/associated-type-bounds/issue-104916.rs
+++ b/tests/ui/associated-type-bounds/issue-104916.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 trait B {
     type AssocType;
 }
diff --git a/tests/ui/associated-type-bounds/issue-104916.stderr b/tests/ui/associated-type-bounds/issue-104916.stderr
index e8618b72103..21927328dad 100644
--- a/tests/ui/associated-type-bounds/issue-104916.stderr
+++ b/tests/ui/associated-type-bounds/issue-104916.stderr
@@ -1,5 +1,5 @@
 error: associated type bounds are not allowed in `dyn` types
-  --> $DIR/issue-104916.rs:9:19
+  --> $DIR/issue-104916.rs:7:19
    |
 LL |     dyn for<'j> B:,
    |                   ^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/issue-61752.rs b/tests/ui/associated-type-bounds/issue-61752.rs
index 22e43ea875e..a73ba833c43 100644
--- a/tests/ui/associated-type-bounds/issue-61752.rs
+++ b/tests/ui/associated-type-bounds/issue-61752.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait Foo {
     type Bar;
 }
diff --git a/tests/ui/associated-type-bounds/issue-70292.rs b/tests/ui/associated-type-bounds/issue-70292.rs
index 4b8e19904d0..1a6bdcd1a31 100644
--- a/tests/ui/associated-type-bounds/issue-70292.rs
+++ b/tests/ui/associated-type-bounds/issue-70292.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 fn foo(_: F)
 where
     F: for<'a> Trait,
diff --git a/tests/ui/associated-type-bounds/issue-71443-1.rs b/tests/ui/associated-type-bounds/issue-71443-1.rs
index 5d2a3e6cbad..58341ac3d3a 100644
--- a/tests/ui/associated-type-bounds/issue-71443-1.rs
+++ b/tests/ui/associated-type-bounds/issue-71443-1.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 struct Incorrect;
 
 fn hello Iterator>() {
diff --git a/tests/ui/associated-type-bounds/issue-71443-1.stderr b/tests/ui/associated-type-bounds/issue-71443-1.stderr
index 6abaaf8e182..27ef545daaa 100644
--- a/tests/ui/associated-type-bounds/issue-71443-1.stderr
+++ b/tests/ui/associated-type-bounds/issue-71443-1.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-71443-1.rs:6:5
+  --> $DIR/issue-71443-1.rs:4:5
    |
 LL | fn hello Iterator>() {
    |                                          - help: try adding a return type: `-> Incorrect`
diff --git a/tests/ui/associated-type-bounds/issue-71443-2.rs b/tests/ui/associated-type-bounds/issue-71443-2.rs
index bd072f44650..24c4c88f20b 100644
--- a/tests/ui/associated-type-bounds/issue-71443-2.rs
+++ b/tests/ui/associated-type-bounds/issue-71443-2.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 fn hello<'b, F>()
 where
     for<'a> F: Iterator + 'b,
diff --git a/tests/ui/associated-type-bounds/issue-79949.rs b/tests/ui/associated-type-bounds/issue-79949.rs
index 4513f0a0b62..9f4a8ffa7e7 100644
--- a/tests/ui/associated-type-bounds/issue-79949.rs
+++ b/tests/ui/associated-type-bounds/issue-79949.rs
@@ -1,8 +1,6 @@
 //@ check-pass
 
 #![allow(incomplete_features)]
-#![feature(associated_type_bounds)]
-
 trait MP {
     type T<'a>;
 }
diff --git a/tests/ui/associated-type-bounds/issue-81193.rs b/tests/ui/associated-type-bounds/issue-81193.rs
index 1247f835be9..caa5915819b 100644
--- a/tests/ui/associated-type-bounds/issue-81193.rs
+++ b/tests/ui/associated-type-bounds/issue-81193.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait A<'a, 'b> {}
 
 trait B<'a, 'b, 'c> {}
diff --git a/tests/ui/associated-type-bounds/issue-83017.rs b/tests/ui/associated-type-bounds/issue-83017.rs
index a059b940e66..932b71cc0ae 100644
--- a/tests/ui/associated-type-bounds/issue-83017.rs
+++ b/tests/ui/associated-type-bounds/issue-83017.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait TraitA<'a> {
     type AsA;
 }
diff --git a/tests/ui/associated-type-bounds/nested-bounds-dont-eliminate-alias-bounds.rs b/tests/ui/associated-type-bounds/nested-bounds-dont-eliminate-alias-bounds.rs
index ee4de509da6..305c117da62 100644
--- a/tests/ui/associated-type-bounds/nested-bounds-dont-eliminate-alias-bounds.rs
+++ b/tests/ui/associated-type-bounds/nested-bounds-dont-eliminate-alias-bounds.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait Trait1 {
     type Assoc1: Bar;
 
diff --git a/tests/ui/associated-type-bounds/no-gat-position.rs b/tests/ui/associated-type-bounds/no-gat-position.rs
index 01740e6242e..5005c5027f4 100644
--- a/tests/ui/associated-type-bounds/no-gat-position.rs
+++ b/tests/ui/associated-type-bounds/no-gat-position.rs
@@ -1,5 +1,3 @@
-#![feature(associated_type_bounds)]
-
 // Test for .
 
 pub trait Iter {
diff --git a/tests/ui/associated-type-bounds/no-gat-position.stderr b/tests/ui/associated-type-bounds/no-gat-position.stderr
index 5692b2c7d09..c348d33c3a9 100644
--- a/tests/ui/associated-type-bounds/no-gat-position.stderr
+++ b/tests/ui/associated-type-bounds/no-gat-position.stderr
@@ -1,5 +1,5 @@
 error[E0229]: associated type bindings are not allowed here
-  --> $DIR/no-gat-position.rs:8:56
+  --> $DIR/no-gat-position.rs:6:56
    |
 LL |     fn next<'a>(&'a mut self) -> Option>;
    |                                                        ^^^^^^^^^ associated type not allowed here
diff --git a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs
index 3853bc8594f..c0012564843 100644
--- a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs
+++ b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.rs
@@ -1,5 +1,4 @@
 #![allow(bare_trait_objects)]
-#![feature(associated_type_bounds)]
 trait Item {
     type Core;
 }
diff --git a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr
index 03d72f2ae2c..39a2b98e2e2 100644
--- a/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr
+++ b/tests/ui/associated-type-bounds/overlaping-bound-suggestion.stderr
@@ -1,11 +1,11 @@
 error[E0191]: the value of the associated types `Item` and `IntoIter` in `IntoIterator` must be specified
-  --> $DIR/overlaping-bound-suggestion.rs:7:13
+  --> $DIR/overlaping-bound-suggestion.rs:6:13
    |
 LL |     inner: >::IntoIterator as Item>::Core,
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated types: `IntoIterator, Item = Type, IntoIter = Type>`
 
 error[E0223]: ambiguous associated type
-  --> $DIR/overlaping-bound-suggestion.rs:7:13
+  --> $DIR/overlaping-bound-suggestion.rs:6:13
    |
 LL |     inner: >::IntoIterator as Item>::Core,
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs
index 50a8cd8e04b..c23eff79ce2 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs
+++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs
@@ -9,11 +9,9 @@ trait Trait {
 
 fn foo>() {}
 //~^ ERROR argument types not allowed with return type notation
-//~| ERROR associated type bounds are unstable
 
 fn bar (): Send>>() {}
 //~^ ERROR return type not allowed with return type notation
-//~| ERROR associated type bounds are unstable
 
 fn baz>() {}
 //~^ ERROR return type notation uses `()` instead of `(..)` for elided arguments
diff --git a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr
index 02bec24c628..d95249efe40 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr
+++ b/tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.stderr
@@ -1,29 +1,9 @@
 error: return type notation uses `()` instead of `(..)` for elided arguments
-  --> $DIR/bad-inputs-and-output.rs:18:24
+  --> $DIR/bad-inputs-and-output.rs:16:24
    |
 LL | fn baz>() {}
    |                        ^^ help: remove the `..`
 
-error[E0658]: associated type bounds are unstable
-  --> $DIR/bad-inputs-and-output.rs:10:17
-   |
-LL | fn foo>() {}
-   |                 ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/bad-inputs-and-output.rs:14:17
-   |
-LL | fn bar (): Send>>() {}
-   |                 ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
   --> $DIR/bad-inputs-and-output.rs:3:12
    |
@@ -40,11 +20,10 @@ LL | fn foo>() {}
    |                       ^^^^^ help: remove the input types: `()`
 
 error: return type not allowed with return type notation
-  --> $DIR/bad-inputs-and-output.rs:14:25
+  --> $DIR/bad-inputs-and-output.rs:13:25
    |
 LL | fn bar (): Send>>() {}
    |                         ^^^^^^ help: remove the return type
 
-error: aborting due to 5 previous errors; 1 warning emitted
+error: aborting due to 3 previous errors; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.rs b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.rs
index 0a98f0d2c8d..931e41bc840 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.rs
+++ b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.rs
@@ -1,11 +1,14 @@
 //@ edition: 2021
 //@ compile-flags: -Zunpretty=expanded
+//@ check-pass
+
+// NOTE: This is not considered RTN syntax currently.
+// This is simply parenthesized generics.
 
 trait Trait {
     async fn method() {}
 }
 
 fn foo>() {}
-//~^ ERROR associated type bounds are unstable
 
 fn main() {}
diff --git a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stderr b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stderr
deleted file mode 100644
index 3007240c3ab..00000000000
--- a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-error[E0658]: associated type bounds are unstable
-  --> $DIR/unpretty-parenthesized.rs:8:17
-   |
-LL | fn foo>() {}
-   |                 ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout
index 17c3b9580ca..87667553837 100644
--- a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout
+++ b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout
@@ -5,6 +5,10 @@ use std::prelude::rust_2021::*;
 extern crate std;
 //@ edition: 2021
 //@ compile-flags: -Zunpretty=expanded
+//@ check-pass
+
+// NOTE: This is not considered RTN syntax currently.
+// This is simply parenthesized generics.
 
 trait Trait {
     async fn method() {}
diff --git a/tests/ui/associated-type-bounds/rpit.rs b/tests/ui/associated-type-bounds/rpit.rs
index 78710621cad..cb1d7f8fcc7 100644
--- a/tests/ui/associated-type-bounds/rpit.rs
+++ b/tests/ui/associated-type-bounds/rpit.rs
@@ -1,7 +1,5 @@
 //@ run-pass
 
-#![feature(associated_type_bounds)]
-
 use std::ops::Add;
 
 trait Tr1 { type As1; fn mk(self) -> Self::As1; }
diff --git a/tests/ui/associated-type-bounds/rpit.stderr b/tests/ui/associated-type-bounds/rpit.stderr
index 76bd75bd2ca..1091a4c573b 100644
--- a/tests/ui/associated-type-bounds/rpit.stderr
+++ b/tests/ui/associated-type-bounds/rpit.stderr
@@ -1,5 +1,5 @@
 warning: method `tr2` is never used
-  --> $DIR/rpit.rs:8:20
+  --> $DIR/rpit.rs:6:20
    |
 LL | trait Tr2<'a> { fn tr2(self) -> &'a Self; }
    |       ---          ^^^
diff --git a/tests/ui/associated-type-bounds/struct-bounds.rs b/tests/ui/associated-type-bounds/struct-bounds.rs
index 2c46832cb99..0c8a52539f3 100644
--- a/tests/ui/associated-type-bounds/struct-bounds.rs
+++ b/tests/ui/associated-type-bounds/struct-bounds.rs
@@ -1,8 +1,6 @@
 //@ run-pass
 
 #![allow(unused)]
-#![feature(associated_type_bounds)]
-
 trait Tr1 { type As1; }
 trait Tr2 { type As2; }
 trait Tr3 {}
diff --git a/tests/ui/associated-type-bounds/supertrait-defines-ty.rs b/tests/ui/associated-type-bounds/supertrait-defines-ty.rs
index 62b23b5fbab..ed1c1fa6f03 100644
--- a/tests/ui/associated-type-bounds/supertrait-defines-ty.rs
+++ b/tests/ui/associated-type-bounds/supertrait-defines-ty.rs
@@ -3,8 +3,6 @@
 // Make sure that we don't look into associated type bounds when looking for
 // supertraits that define an associated type. Fixes #76593.
 
-#![feature(associated_type_bounds)]
-
 trait Load: Sized {
     type Blob;
 }
diff --git a/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs b/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs
index 6ca9f80ccaf..fb6a4fcbe97 100644
--- a/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs
+++ b/tests/ui/associated-type-bounds/trait-alias-impl-trait.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 
 #![allow(dead_code)]
-#![feature(associated_type_bounds)]
 #![feature(type_alias_impl_trait)]
 
 use std::ops::Add;
diff --git a/tests/ui/associated-type-bounds/trait-params.rs b/tests/ui/associated-type-bounds/trait-params.rs
index 6782d688126..72a445351e7 100644
--- a/tests/ui/associated-type-bounds/trait-params.rs
+++ b/tests/ui/associated-type-bounds/trait-params.rs
@@ -1,7 +1,5 @@
 //@ build-pass (FIXME(62277): could be check-pass?)
 
-#![feature(associated_type_bounds)]
-
 use std::iter::Once;
 use std::ops::Range;
 
diff --git a/tests/ui/associated-type-bounds/type-alias.rs b/tests/ui/associated-type-bounds/type-alias.rs
index 819a7656a44..2dccb37f37f 100644
--- a/tests/ui/associated-type-bounds/type-alias.rs
+++ b/tests/ui/associated-type-bounds/type-alias.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 type _TaWhere1 where T: Iterator = T; //~ WARNING type_alias_bounds
 type _TaWhere2 where T: Iterator = T; //~ WARNING type_alias_bounds
 type _TaWhere3 where T: Iterator = T; //~ WARNING type_alias_bounds
diff --git a/tests/ui/associated-type-bounds/type-alias.stderr b/tests/ui/associated-type-bounds/type-alias.stderr
index c22b80b889e..072c471467c 100644
--- a/tests/ui/associated-type-bounds/type-alias.stderr
+++ b/tests/ui/associated-type-bounds/type-alias.stderr
@@ -1,5 +1,5 @@
 warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias.rs:5:25
+  --> $DIR/type-alias.rs:3:25
    |
 LL | type _TaWhere1 where T: Iterator = T;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^
@@ -12,7 +12,7 @@ LL + type _TaWhere1  = T;
    |
 
 warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias.rs:6:25
+  --> $DIR/type-alias.rs:4:25
    |
 LL | type _TaWhere2 where T: Iterator = T;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL + type _TaWhere2  = T;
    |
 
 warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias.rs:7:25
+  --> $DIR/type-alias.rs:5:25
    |
 LL | type _TaWhere3 where T: Iterator = T;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL + type _TaWhere3  = T;
    |
 
 warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias.rs:8:25
+  --> $DIR/type-alias.rs:6:25
    |
 LL | type _TaWhere4 where T: Iterator = T;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL + type _TaWhere4  = T;
    |
 
 warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias.rs:9:25
+  --> $DIR/type-alias.rs:7:25
    |
 LL | type _TaWhere5 where T: Iterator Into<&'a u8>> = T;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL + type _TaWhere5  = T;
    |
 
 warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias.rs:10:25
+  --> $DIR/type-alias.rs:8:25
    |
 LL | type _TaWhere6 where T: Iterator> = T;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL + type _TaWhere6  = T;
    |
 
 warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias.rs:12:20
+  --> $DIR/type-alias.rs:10:20
    |
 LL | type _TaInline1> = T;
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -84,7 +84,7 @@ LL + type _TaInline1 = T;
    |
 
 warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias.rs:13:20
+  --> $DIR/type-alias.rs:11:20
    |
 LL | type _TaInline2> = T;
    |                    ^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,7 +96,7 @@ LL + type _TaInline2 = T;
    |
 
 warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias.rs:14:20
+  --> $DIR/type-alias.rs:12:20
    |
 LL | type _TaInline3> = T;
    |                    ^^^^^^^^^^^^^^^^^^^^^^^
@@ -108,7 +108,7 @@ LL + type _TaInline3 = T;
    |
 
 warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias.rs:15:20
+  --> $DIR/type-alias.rs:13:20
    |
 LL | type _TaInline4> = T;
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL + type _TaInline4 = T;
    |
 
 warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias.rs:16:20
+  --> $DIR/type-alias.rs:14:20
    |
 LL | type _TaInline5 Into<&'a u8>>> = T;
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +132,7 @@ LL + type _TaInline5 = T;
    |
 
 warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias.rs:17:20
+  --> $DIR/type-alias.rs:15:20
    |
 LL | type _TaInline6>> = T;
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/associated-type-bounds/union-bounds.rs b/tests/ui/associated-type-bounds/union-bounds.rs
index 8a7ba6f5ebf..b9b92a96fb0 100644
--- a/tests/ui/associated-type-bounds/union-bounds.rs
+++ b/tests/ui/associated-type-bounds/union-bounds.rs
@@ -1,7 +1,5 @@
 //@ run-pass
 
-#![feature(associated_type_bounds)]
-
 #![allow(unused_assignments)]
 
 trait Tr1: Copy { type As1: Copy; }
diff --git a/tests/ui/associated-types/issue-63591.rs b/tests/ui/associated-types/issue-63591.rs
index 33826a24ddb..52464d94a23 100644
--- a/tests/ui/associated-types/issue-63591.rs
+++ b/tests/ui/associated-types/issue-63591.rs
@@ -1,6 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
 #![feature(impl_trait_in_assoc_type)]
 
 fn main() {}
diff --git a/tests/ui/borrowck/mut-borrow-in-loop-2.fixed b/tests/ui/borrowck/mut-borrow-in-loop-2.fixed
deleted file mode 100644
index cff3c372cdb..00000000000
--- a/tests/ui/borrowck/mut-borrow-in-loop-2.fixed
+++ /dev/null
@@ -1,35 +0,0 @@
-//@ run-rustfix
-#![allow(dead_code)]
-
-struct Events(R);
-
-struct Other;
-
-pub trait Trait {
-    fn handle(value: T) -> Self;
-}
-
-// Blanket impl. (If you comment this out, compiler figures out that it
-// is passing an `&mut` to a method that must be expecting an `&mut`,
-// and injects an auto-reborrow.)
-impl Trait for T where T: From {
-    fn handle(_: U) -> Self { unimplemented!() }
-}
-
-impl<'a, R> Trait<&'a mut Events> for Other {
-    fn handle(_: &'a mut Events) -> Self { unimplemented!() }
-}
-
-fn this_compiles<'a, R>(value: &'a mut Events) {
-    for _ in 0..3 {
-        Other::handle(&mut *value);
-    }
-}
-
-fn this_does_not<'a, R>(value: &'a mut Events) {
-    for _ in 0..3 {
-        Other::handle(&mut *value); //~ ERROR use of moved value: `value`
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/borrowck/mut-borrow-in-loop-2.rs b/tests/ui/borrowck/mut-borrow-in-loop-2.rs
index ba79b12042f..f530dfca1a3 100644
--- a/tests/ui/borrowck/mut-borrow-in-loop-2.rs
+++ b/tests/ui/borrowck/mut-borrow-in-loop-2.rs
@@ -1,4 +1,3 @@
-//@ run-rustfix
 #![allow(dead_code)]
 
 struct Events(R);
diff --git a/tests/ui/borrowck/mut-borrow-in-loop-2.stderr b/tests/ui/borrowck/mut-borrow-in-loop-2.stderr
index 7b9a946f3ca..7a569d1da41 100644
--- a/tests/ui/borrowck/mut-borrow-in-loop-2.stderr
+++ b/tests/ui/borrowck/mut-borrow-in-loop-2.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `value`
-  --> $DIR/mut-borrow-in-loop-2.rs:31:23
+  --> $DIR/mut-borrow-in-loop-2.rs:30:23
    |
 LL | fn this_does_not<'a, R>(value: &'a mut Events) {
    |                         ----- move occurs because `value` has type `&mut Events`, which does not implement the `Copy` trait
@@ -9,12 +9,18 @@ LL |         Other::handle(value);
    |                       ^^^^^ value moved here, in previous iteration of loop
    |
 note: consider changing this parameter type in function `handle` to borrow instead if owning the value isn't necessary
-  --> $DIR/mut-borrow-in-loop-2.rs:9:22
+  --> $DIR/mut-borrow-in-loop-2.rs:8:22
    |
 LL |     fn handle(value: T) -> Self;
    |        ------        ^ this parameter takes ownership of the value
    |        |
    |        in this function
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~     let mut value = Other::handle(value);
+LL ~     for _ in 0..3 {
+LL ~         value;
+   |
 help: consider creating a fresh reborrow of `value` here
    |
 LL |         Other::handle(&mut *value);
diff --git a/tests/ui/check-static-values-constraints.stderr b/tests/ui/check-static-values-constraints.stderr
index e7532de5647..dee1f2b1210 100644
--- a/tests/ui/check-static-values-constraints.stderr
+++ b/tests/ui/check-static-values-constraints.stderr
@@ -36,8 +36,11 @@ LL |     field2: SafeEnum::Variant4("str".to_string()),
    |                                      ^^^^^^^^^^^
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-static-values-constraints.rs:96:5
diff --git a/tests/ui/codegen/target-cpus.rs b/tests/ui/codegen/target-cpus.rs
index 85a940f9f74..2d46e00f803 100644
--- a/tests/ui/codegen/target-cpus.rs
+++ b/tests/ui/codegen/target-cpus.rs
@@ -1,4 +1,3 @@
 //@ needs-llvm-components: webassembly
-//@ min-llvm-version: 17
 //@ compile-flags: --print=target-cpus --target=wasm32-unknown-unknown
 //@ check-pass
diff --git a/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.rs b/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.rs
index a83830178d4..0d2e65c45ea 100644
--- a/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.rs
+++ b/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.rs
@@ -5,11 +5,16 @@
 // Can never be used as const generics.
 fn uwu_0() {}
 //~^ ERROR: forbidden as the type of a const generic
+//~| HELP: add `#![feature(adt_const_params)]`
+//~| HELP: add `#![feature(adt_const_params)]`
+//~| HELP: add `#![feature(adt_const_params)]`
+//~| HELP: add `#![feature(adt_const_params)]`
+//~| HELP: add `#![feature(adt_const_params)]`
+//~| HELP: add `#![feature(adt_const_params)]`
 
 // Needs the feature but can be used, so suggest adding the feature.
 fn owo_0() {}
 //~^ ERROR: forbidden as the type of a const generic
-//~^^ HELP: add `#![feature(adt_const_params)]`
 
 // Can only be used in const generics with changes.
 struct Meow {
@@ -18,22 +23,17 @@ struct Meow {
 
 fn meow_0() {}
 //~^ ERROR: forbidden as the type of a const generic
-//~^^ HELP: add `#![feature(adt_const_params)]`
 fn meow_1() {}
 //~^ ERROR: forbidden as the type of a const generic
-//~^^ HELP: add `#![feature(adt_const_params)]`
 fn meow_2() {}
 //~^ ERROR: forbidden as the type of a const generic
-//~^^ HELP: add `#![feature(adt_const_params)]`
 fn meow_3() {}
 //~^ ERROR: forbidden as the type of a const generic
-//~^^ HELP: add `#![feature(adt_const_params)]`
 
 // This is suboptimal that it thinks it can be used
 // but better to suggest the feature to the user.
 fn meow_4() {}
 //~^ ERROR: forbidden as the type of a const generic
-//~^^ HELP: add `#![feature(adt_const_params)]`
 
 // Non-local ADT that does not impl `ConstParamTy`
 fn nya_0() {}
diff --git a/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr b/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr
index 04527e3158e..cd4349623d7 100644
--- a/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr
+++ b/tests/ui/const-generics/adt_const_params/suggest_feature_only_when_possible.stderr
@@ -7,58 +7,76 @@ LL | fn uwu_0() {}
    = note: the only supported types are integers, `bool` and `char`
 
 error: `&'static u32` is forbidden as the type of a const generic parameter
-  --> $DIR/suggest_feature_only_when_possible.rs:10:19
+  --> $DIR/suggest_feature_only_when_possible.rs:16:19
    |
 LL | fn owo_0() {}
    |                   ^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `Meow` is forbidden as the type of a const generic parameter
-  --> $DIR/suggest_feature_only_when_possible.rs:19:20
+  --> $DIR/suggest_feature_only_when_possible.rs:24:20
    |
 LL | fn meow_0() {}
    |                    ^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&'static Meow` is forbidden as the type of a const generic parameter
-  --> $DIR/suggest_feature_only_when_possible.rs:22:20
+  --> $DIR/suggest_feature_only_when_possible.rs:26:20
    |
 LL | fn meow_1() {}
    |                    ^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `[Meow; 100]` is forbidden as the type of a const generic parameter
-  --> $DIR/suggest_feature_only_when_possible.rs:25:20
+  --> $DIR/suggest_feature_only_when_possible.rs:28:20
    |
 LL | fn meow_2() {}
    |                    ^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `(Meow, u8)` is forbidden as the type of a const generic parameter
-  --> $DIR/suggest_feature_only_when_possible.rs:28:20
+  --> $DIR/suggest_feature_only_when_possible.rs:30:20
    |
 LL | fn meow_3() {}
    |                    ^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `(Meow, String)` is forbidden as the type of a const generic parameter
-  --> $DIR/suggest_feature_only_when_possible.rs:34:20
+  --> $DIR/suggest_feature_only_when_possible.rs:35:20
    |
 LL | fn meow_4() {}
    |                    ^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `String` is forbidden as the type of a const generic parameter
   --> $DIR/suggest_feature_only_when_possible.rs:39:19
diff --git a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr
index ffe45285988..1c81b14f8f5 100644
--- a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr
+++ b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr
@@ -35,7 +35,10 @@ LL | struct A;
    |                   ^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&u8` is forbidden as the type of a const generic parameter
   --> $DIR/const-param-elided-lifetime.rs:14:15
@@ -44,7 +47,10 @@ LL | impl A {
    |               ^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&u8` is forbidden as the type of a const generic parameter
   --> $DIR/const-param-elided-lifetime.rs:22:15
@@ -53,7 +59,10 @@ LL | impl B for A {}
    |               ^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&u8` is forbidden as the type of a const generic parameter
   --> $DIR/const-param-elided-lifetime.rs:26:17
@@ -62,7 +71,10 @@ LL | fn bar() {}
    |                 ^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&u8` is forbidden as the type of a const generic parameter
   --> $DIR/const-param-elided-lifetime.rs:17:21
@@ -71,7 +83,10 @@ LL |     fn foo(&self) {}
    |                     ^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 10 previous errors
 
diff --git a/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr b/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr
index daeeadeed7c..fcc86b9ac33 100644
--- a/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr
+++ b/tests/ui/const-generics/const-param-type-depends-on-const-param.min.stderr
@@ -21,7 +21,10 @@ LL | pub struct Dependent([(); N]);
    |                                               ^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `[u8; N]` is forbidden as the type of a const generic parameter
   --> $DIR/const-param-type-depends-on-const-param.rs:15:35
@@ -30,7 +33,10 @@ LL | pub struct SelfDependent;
    |                                   ^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr
index 1f4b892e20f..1f67a5c09f1 100644
--- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr
@@ -23,7 +23,10 @@ LL | struct B {
    |                     ^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr
index 77a7da17c13..0bf99bb8b26 100644
--- a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr
@@ -54,7 +54,10 @@ note: impl defined here, but it is not `const`
 LL | impl const std::ops::Add for Foo {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const fn `::add` in constants
   --> $DIR/unify-op-with-fn-call.rs:21:13
@@ -63,7 +66,10 @@ LL |     bar::<{ std::ops::Add::add(N, N) }>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const fn `::add` in constants
   --> $DIR/unify-op-with-fn-call.rs:30:14
@@ -72,7 +78,10 @@ LL |     bar2::<{ std::ops::Add::add(N, N) }>();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr b/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr
index 95f75c32186..5e4acd80e93 100644
--- a/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr
+++ b/tests/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr
@@ -14,7 +14,10 @@ LL | trait Trait {}
    |                      ^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const-generics/issue-93647.stderr b/tests/ui/const-generics/issue-93647.stderr
index a87b59940cb..81f50a1b517 100644
--- a/tests/ui/const-generics/issue-93647.stderr
+++ b/tests/ui/const-generics/issue-93647.stderr
@@ -6,7 +6,10 @@ LL |     (||1usize)()
    |
    = note: closures need an RFC before allowed to be called in constants
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-56445-1.min.stderr b/tests/ui/const-generics/issues/issue-56445-1.min.stderr
index fc10aba0fec..580542bb6da 100644
--- a/tests/ui/const-generics/issues/issue-56445-1.min.stderr
+++ b/tests/ui/const-generics/issues/issue-56445-1.min.stderr
@@ -13,7 +13,10 @@ LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
    |                         ^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const-generics/issues/issue-62878.min.stderr b/tests/ui/const-generics/issues/issue-62878.min.stderr
index 984381d1669..5205726d738 100644
--- a/tests/ui/const-generics/issues/issue-62878.min.stderr
+++ b/tests/ui/const-generics/issues/issue-62878.min.stderr
@@ -13,7 +13,10 @@ LL | fn foo() {}
    |                                 ^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error[E0747]: type provided when a constant was expected
   --> $DIR/issue-62878.rs:10:11
@@ -22,7 +25,10 @@ LL |     foo::<_, { [1] }>();
    |           ^
    |
    = help: const arguments cannot yet be inferred with `_`
-   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+   |
+LL + #![feature(generic_arg_infer)]
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr
index b9588e23e55..7f387cbd5a1 100644
--- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr
+++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr
@@ -5,7 +5,10 @@ LL | fn test() {
    |                  ^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-67185-2.stderr b/tests/ui/const-generics/issues/issue-67185-2.stderr
index 24a2d60f2e1..e39d43205c1 100644
--- a/tests/ui/const-generics/issues/issue-67185-2.stderr
+++ b/tests/ui/const-generics/issues/issue-67185-2.stderr
@@ -8,7 +8,10 @@ LL |     ::Quaks: Bar,
              [u16; 4]
              [[u16; 3]; 3]
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
   --> $DIR/issue-67185-2.rs:14:5
@@ -20,7 +23,10 @@ LL |     [::Quaks; 2]: Bar,
              [u16; 4]
              [[u16; 3]; 3]
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
   --> $DIR/issue-67185-2.rs:21:6
diff --git a/tests/ui/const-generics/issues/issue-68366.full.stderr b/tests/ui/const-generics/issues/issue-68366.full.stderr
index dc20af77310..3363a895e47 100644
--- a/tests/ui/const-generics/issues/issue-68366.full.stderr
+++ b/tests/ui/const-generics/issues/issue-68366.full.stderr
@@ -5,7 +5,10 @@ LL | struct Collatz>;
    |                         ^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
   --> $DIR/issue-68366.rs:12:7
diff --git a/tests/ui/const-generics/issues/issue-68366.min.stderr b/tests/ui/const-generics/issues/issue-68366.min.stderr
index 78e49f46e1a..276f91e76dd 100644
--- a/tests/ui/const-generics/issues/issue-68366.min.stderr
+++ b/tests/ui/const-generics/issues/issue-68366.min.stderr
@@ -14,7 +14,10 @@ LL | struct Collatz>;
    |                         ^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
   --> $DIR/issue-68366.rs:12:7
diff --git a/tests/ui/const-generics/issues/issue-68615-adt.min.stderr b/tests/ui/const-generics/issues/issue-68615-adt.min.stderr
index 20962098bff..2f95eef98c0 100644
--- a/tests/ui/const-generics/issues/issue-68615-adt.min.stderr
+++ b/tests/ui/const-generics/issues/issue-68615-adt.min.stderr
@@ -5,7 +5,10 @@ LL | struct Const {}
    |                       ^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-68615-array.min.stderr b/tests/ui/const-generics/issues/issue-68615-array.min.stderr
index 8c76f9b5d65..6d18f8195d2 100644
--- a/tests/ui/const-generics/issues/issue-68615-array.min.stderr
+++ b/tests/ui/const-generics/issues/issue-68615-array.min.stderr
@@ -5,7 +5,10 @@ LL | struct Foo {}
    |                     ^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-71169.min.stderr b/tests/ui/const-generics/issues/issue-71169.min.stderr
index bba92f32a78..94d11f969ff 100644
--- a/tests/ui/const-generics/issues/issue-71169.min.stderr
+++ b/tests/ui/const-generics/issues/issue-71169.min.stderr
@@ -13,7 +13,10 @@ LL | fn foo() {}
    |                                      ^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const-generics/issues/issue-73491.min.stderr b/tests/ui/const-generics/issues/issue-73491.min.stderr
index 64df76756ac..8fdd65894ef 100644
--- a/tests/ui/const-generics/issues/issue-73491.min.stderr
+++ b/tests/ui/const-generics/issues/issue-73491.min.stderr
@@ -5,7 +5,10 @@ LL | fn hoge() {}
    |                   ^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr b/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr
index 2b33f35defd..e9363d42148 100644
--- a/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr
+++ b/tests/ui/const-generics/issues/issue-73727-static-reference-array-const-param.min.stderr
@@ -5,7 +5,10 @@ LL | fn a() {}
    |               ^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-74101.min.stderr b/tests/ui/const-generics/issues/issue-74101.min.stderr
index 7852ce5bcfc..236556addce 100644
--- a/tests/ui/const-generics/issues/issue-74101.min.stderr
+++ b/tests/ui/const-generics/issues/issue-74101.min.stderr
@@ -5,7 +5,10 @@ LL | fn test() {}
    |                  ^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
   --> $DIR/issue-74101.rs:9:21
@@ -14,7 +17,10 @@ LL | struct Foo;
    |                     ^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const-generics/issues/issue-74255.min.stderr b/tests/ui/const-generics/issues/issue-74255.min.stderr
index 63d8fc12fa4..800902860a7 100644
--- a/tests/ui/const-generics/issues/issue-74255.min.stderr
+++ b/tests/ui/const-generics/issues/issue-74255.min.stderr
@@ -5,7 +5,10 @@ LL |     fn ice_struct_fn() {}
    |                               ^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-74950.min.stderr b/tests/ui/const-generics/issues/issue-74950.min.stderr
index a573dac6087..086176d9959 100644
--- a/tests/ui/const-generics/issues/issue-74950.min.stderr
+++ b/tests/ui/const-generics/issues/issue-74950.min.stderr
@@ -5,7 +5,10 @@ LL | struct Outer;
    |                       ^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `Inner` is forbidden as the type of a const generic parameter
   --> $DIR/issue-74950.rs:19:23
@@ -14,8 +17,11 @@ LL | struct Outer;
    |                       ^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `Inner` is forbidden as the type of a const generic parameter
   --> $DIR/issue-74950.rs:19:23
@@ -24,8 +30,11 @@ LL | struct Outer;
    |                       ^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `Inner` is forbidden as the type of a const generic parameter
   --> $DIR/issue-74950.rs:19:23
@@ -34,8 +43,11 @@ LL | struct Outer;
    |                       ^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/const-generics/issues/issue-75047.min.stderr b/tests/ui/const-generics/issues/issue-75047.min.stderr
index 1d7ac1b0111..f2cc76b9bed 100644
--- a/tests/ui/const-generics/issues/issue-75047.min.stderr
+++ b/tests/ui/const-generics/issues/issue-75047.min.stderr
@@ -5,7 +5,10 @@ LL | struct Foo::value()]>;
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-90318.stderr b/tests/ui/const-generics/issues/issue-90318.stderr
index 471a6660ce0..a534e8f8d44 100644
--- a/tests/ui/const-generics/issues/issue-90318.stderr
+++ b/tests/ui/const-generics/issues/issue-90318.stderr
@@ -29,7 +29,10 @@ LL |     If<{ TypeId::of::() != TypeId::of::<()>() }>: True,
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/any.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const operator in constants
   --> $DIR/issue-90318.rs:22:10
@@ -40,7 +43,10 @@ LL |     If<{ TypeId::of::() != TypeId::of::<()>() }>: True,
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/any.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/const-generics/lifetime-in-const-param.stderr b/tests/ui/const-generics/lifetime-in-const-param.stderr
index c2fcdcf1a71..4096725c52a 100644
--- a/tests/ui/const-generics/lifetime-in-const-param.stderr
+++ b/tests/ui/const-generics/lifetime-in-const-param.stderr
@@ -11,7 +11,10 @@ LL | struct S<'a, const N: S2>(&'a ());
    |                       ^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const-generics/min_const_generics/complex-types.stderr b/tests/ui/const-generics/min_const_generics/complex-types.stderr
index 8cc75dbaff9..8e83ea58194 100644
--- a/tests/ui/const-generics/min_const_generics/complex-types.stderr
+++ b/tests/ui/const-generics/min_const_generics/complex-types.stderr
@@ -5,7 +5,10 @@ LL | struct Foo;
    |                     ^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `()` is forbidden as the type of a const generic parameter
   --> $DIR/complex-types.rs:6:21
@@ -14,7 +17,10 @@ LL | struct Bar;
    |                     ^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `No` is forbidden as the type of a const generic parameter
   --> $DIR/complex-types.rs:11:21
@@ -23,7 +29,10 @@ LL | struct Fez;
    |                     ^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&'static u8` is forbidden as the type of a const generic parameter
   --> $DIR/complex-types.rs:14:21
@@ -32,7 +41,10 @@ LL | struct Faz;
    |                     ^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `!` is forbidden as the type of a const generic parameter
   --> $DIR/complex-types.rs:17:21
@@ -49,7 +61,10 @@ LL | enum Goo { A, B }
    |                   ^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `()` is forbidden as the type of a const generic parameter
   --> $DIR/complex-types.rs:23:20
@@ -58,7 +73,10 @@ LL | union Boo { a: () }
    |                    ^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/const-generics/nested-type.min.stderr b/tests/ui/const-generics/nested-type.min.stderr
index ca5af5f969f..0da2b30e3f1 100644
--- a/tests/ui/const-generics/nested-type.min.stderr
+++ b/tests/ui/const-generics/nested-type.min.stderr
@@ -30,7 +30,10 @@ LL | | }]>;
    | |__^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const-generics/slice-const-param-mismatch.min.stderr b/tests/ui/const-generics/slice-const-param-mismatch.min.stderr
index 26f5af6c831..0650dafc685 100644
--- a/tests/ui/const-generics/slice-const-param-mismatch.min.stderr
+++ b/tests/ui/const-generics/slice-const-param-mismatch.min.stderr
@@ -5,7 +5,10 @@ LL | struct ConstString;
    |                             ^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&'static [u8]` is forbidden as the type of a const generic parameter
   --> $DIR/slice-const-param-mismatch.rs:9:28
@@ -14,7 +17,10 @@ LL | struct ConstBytes;
    |                            ^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error[E0308]: mismatched types
   --> $DIR/slice-const-param-mismatch.rs:14:35
diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr
index d45f749246c..67f137cf1a0 100644
--- a/tests/ui/const-generics/std/const-generics-range.min.stderr
+++ b/tests/ui/const-generics/std/const-generics-range.min.stderr
@@ -5,7 +5,10 @@ LL | struct _Range>;
    |                        ^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `RangeFrom` is forbidden as the type of a const generic parameter
   --> $DIR/const-generics-range.rs:13:28
@@ -14,7 +17,10 @@ LL | struct _RangeFrom>;
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `RangeFull` is forbidden as the type of a const generic parameter
   --> $DIR/const-generics-range.rs:18:28
@@ -23,7 +29,10 @@ LL | struct _RangeFull;
    |                            ^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `RangeInclusive` is forbidden as the type of a const generic parameter
   --> $DIR/const-generics-range.rs:24:33
@@ -32,7 +41,10 @@ LL | struct _RangeInclusive>;
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `RangeTo` is forbidden as the type of a const generic parameter
   --> $DIR/const-generics-range.rs:29:26
@@ -41,7 +53,10 @@ LL | struct _RangeTo>;
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `RangeToInclusive` is forbidden as the type of a const generic parameter
   --> $DIR/const-generics-range.rs:34:35
@@ -50,7 +65,10 @@ LL | struct _RangeToInclusive>;
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr b/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr
index 25bb7ac8039..fdb6ddeb578 100644
--- a/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr
+++ b/tests/ui/const-generics/transmute-const-param-static-reference.min.stderr
@@ -5,7 +5,10 @@ LL | struct Const;
    |                       ^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr
index 6490592c1e1..f42a331a8a4 100644
--- a/tests/ui/const-generics/type-dependent/issue-71348.min.stderr
+++ b/tests/ui/const-generics/type-dependent/issue-71348.min.stderr
@@ -5,7 +5,10 @@ LL | trait Get<'a, const N: &'static str> {
    |                        ^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: `&'static str` is forbidden as the type of a const generic parameter
   --> $DIR/issue-71348.rs:18:25
@@ -14,7 +17,10 @@ LL |     fn ask<'a, const N: &'static str>(&'a self) -> &'a >::Ta
    |                         ^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/const-fn-error.stderr b/tests/ui/consts/const-fn-error.stderr
index 68c335c71d9..14603e433c1 100644
--- a/tests/ui/consts/const-fn-error.stderr
+++ b/tests/ui/consts/const-fn-error.stderr
@@ -23,7 +23,10 @@ LL |     for i in 0..x {
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0658]: mutable references are not allowed in constant functions
   --> $DIR/const-fn-error.rs:5:14
@@ -42,7 +45,10 @@ LL |     for i in 0..x {
    |              ^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/consts/const-for-feature-gate.stderr b/tests/ui/consts/const-for-feature-gate.stderr
index 413d144ca0a..0f2f912572e 100644
--- a/tests/ui/consts/const-for-feature-gate.stderr
+++ b/tests/ui/consts/const-for-feature-gate.stderr
@@ -17,7 +17,10 @@ LL |     for _ in 0..5 {}
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0658]: mutable references are not allowed in constants
   --> $DIR/const-for-feature-gate.rs:4:14
@@ -36,7 +39,10 @@ LL |     for _ in 0..5 {}
    |              ^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/consts/const-for.stderr b/tests/ui/consts/const-for.stderr
index 3fb9787c0d8..8605fb8eef5 100644
--- a/tests/ui/consts/const-for.stderr
+++ b/tests/ui/consts/const-for.stderr
@@ -7,7 +7,10 @@ LL |     for _ in 0..5 {}
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const fn ` as Iterator>::next` in constants
   --> $DIR/const-for.rs:5:14
@@ -16,7 +19,10 @@ LL |     for _ in 0..5 {}
    |              ^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/const-try-feature-gate.stderr b/tests/ui/consts/const-try-feature-gate.stderr
index efa1fb107f6..0c4c16fc56a 100644
--- a/tests/ui/consts/const-try-feature-gate.stderr
+++ b/tests/ui/consts/const-try-feature-gate.stderr
@@ -17,7 +17,10 @@ LL |     Some(())?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: `?` cannot convert from residual of `Option<()>` in constant functions
   --> $DIR/const-try-feature-gate.rs:4:5
@@ -28,7 +31,10 @@ LL |     Some(())?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/const-try.stderr b/tests/ui/consts/const-try.stderr
index 37a6598af9e..2d91424c8d3 100644
--- a/tests/ui/consts/const-try.stderr
+++ b/tests/ui/consts/const-try.stderr
@@ -10,7 +10,10 @@ note: impl defined here, but it is not `const`
 LL | impl const Try for TryMe {
    | ^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: `?` cannot convert from residual of `TryMe` in constant functions
   --> $DIR/const-try.rs:33:5
@@ -24,7 +27,10 @@ note: impl defined here, but it is not `const`
 LL | impl const FromResidual for TryMe {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/constifconst-call-in-const-position.stderr b/tests/ui/consts/constifconst-call-in-const-position.stderr
index 42ad4125824..09827f29baf 100644
--- a/tests/ui/consts/constifconst-call-in-const-position.stderr
+++ b/tests/ui/consts/constifconst-call-in-const-position.stderr
@@ -14,7 +14,10 @@ LL |     [0; T::a()]
    |         ^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const fn `::a` in constants
   --> $DIR/constifconst-call-in-const-position.rs:16:38
@@ -23,7 +26,10 @@ LL | const fn foo() -> [u8; T::a()] {
    |                                      ^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors; 1 warning emitted
 
diff --git a/tests/ui/consts/control-flow/dead_branches_dont_eval.rs b/tests/ui/consts/control-flow/dead_branches_dont_eval.rs
new file mode 100644
index 00000000000..374349732f9
--- /dev/null
+++ b/tests/ui/consts/control-flow/dead_branches_dont_eval.rs
@@ -0,0 +1,46 @@
+//@ build-pass
+
+// issue 122301 - currently the only way to supress
+// const eval and codegen of code conditional on some other const
+
+struct Foo(T);
+
+impl Foo {
+    const BAR: () = if N == 0 {
+        panic!()
+    };
+}
+
+struct Invoke(T);
+
+impl Invoke {
+    const FUN: fn() = if N != 0 {
+        || Foo::::BAR
+    } else {
+        || {}
+    };
+}
+
+// without closures
+
+struct S(T);
+impl S {
+    const C: () = panic!();
+}
+
+const fn bar() { S::::C }
+
+struct ConstIf(T);
+
+impl ConstIf {
+    const VAL: () = if N != 0 {
+        bar::() // not called for N == 0, and hence not monomorphized
+    } else {
+        ()
+    };
+}
+
+fn main() {
+    let _val = Invoke::<(), 0>::FUN();
+    let _val = ConstIf::<(), 0>::VAL;
+}
diff --git a/tests/ui/consts/control-flow/loop.stderr b/tests/ui/consts/control-flow/loop.stderr
index e162a404ace..2815b888ccd 100644
--- a/tests/ui/consts/control-flow/loop.stderr
+++ b/tests/ui/consts/control-flow/loop.stderr
@@ -37,7 +37,10 @@ LL |     for i in 0..4 {
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0658]: mutable references are not allowed in constants
   --> $DIR/loop.rs:53:14
@@ -56,7 +59,10 @@ LL |     for i in 0..4 {
    |              ^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot convert `std::ops::Range` into an iterator in constants
   --> $DIR/loop.rs:60:14
@@ -67,7 +73,10 @@ LL |     for i in 0..4 {
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0658]: mutable references are not allowed in constants
   --> $DIR/loop.rs:60:14
@@ -86,7 +95,10 @@ LL |     for i in 0..4 {
    |              ^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 8 previous errors
 
diff --git a/tests/ui/consts/control-flow/try.stderr b/tests/ui/consts/control-flow/try.stderr
index f4c42c4d819..e08f52369fa 100644
--- a/tests/ui/consts/control-flow/try.stderr
+++ b/tests/ui/consts/control-flow/try.stderr
@@ -17,7 +17,10 @@ LL |     x?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: `?` cannot convert from residual of `Option` in constant functions
   --> $DIR/try.rs:6:5
@@ -28,7 +31,10 @@ LL |     x?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr
index 527579d99ea..aad0ae64e85 100644
--- a/tests/ui/consts/fn_trait_refs.stderr
+++ b/tests/ui/consts/fn_trait_refs.stderr
@@ -81,7 +81,10 @@ LL |         assert!(test_one == (1, 1, 1));
    |                 ^^^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const operator in constants
   --> $DIR/fn_trait_refs.rs:75:17
@@ -90,7 +93,10 @@ LL |         assert!(test_two == (2, 2));
    |                 ^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const closure in constant functions
   --> $DIR/fn_trait_refs.rs:17:5
@@ -99,11 +105,14 @@ LL |     f()
    |     ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL |     T: ~const Fn<()> + ~const Destruct + ~const std::ops::Fn<()>,
    |                                        +++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/fn_trait_refs.rs:13:23
@@ -121,11 +130,14 @@ LL |     f()
    |     ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL |     T: ~const FnMut<()> + ~const Destruct + ~const std::ops::FnMut<()>,
    |                                           ++++++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/fn_trait_refs.rs:20:27
@@ -143,11 +155,14 @@ LL |     f()
    |     ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL |     T: ~const FnOnce<()> + ~const std::ops::FnOnce<()>,
    |                          +++++++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0493]: destructor of `T` cannot be evaluated at compile-time
   --> $DIR/fn_trait_refs.rs:34:21
diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr
index a88c16158f3..7579f7f9692 100644
--- a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr
+++ b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr
@@ -6,7 +6,10 @@ LL |         const { (|| {})() } => {}
    |
    = note: closures need an RFC before allowed to be called in constants
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/issue-103790.stderr b/tests/ui/consts/issue-103790.stderr
index abe7366483b..eecaf5ff63a 100644
--- a/tests/ui/consts/issue-103790.stderr
+++ b/tests/ui/consts/issue-103790.stderr
@@ -62,7 +62,10 @@ LL | struct S;
    |                   ^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/consts/issue-28113.stderr b/tests/ui/consts/issue-28113.stderr
index 01bbe4bc9b9..c2f53870173 100644
--- a/tests/ui/consts/issue-28113.stderr
+++ b/tests/ui/consts/issue-28113.stderr
@@ -6,7 +6,10 @@ LL |     || -> u8 { 5 }()
    |
    = note: closures need an RFC before allowed to be called in constants
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/issue-56164.stderr b/tests/ui/consts/issue-56164.stderr
index 1b267214a02..6ec4ce0fbd7 100644
--- a/tests/ui/consts/issue-56164.stderr
+++ b/tests/ui/consts/issue-56164.stderr
@@ -6,7 +6,10 @@ LL | const fn foo() { (||{})() }
    |
    = note: closures need an RFC before allowed to be called in constant functions
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: function pointer calls are not allowed in constant functions
   --> $DIR/issue-56164.rs:5:5
diff --git a/tests/ui/consts/issue-68542-closure-in-array-len.stderr b/tests/ui/consts/issue-68542-closure-in-array-len.stderr
index 3c0408cbedf..b414a6e0dba 100644
--- a/tests/ui/consts/issue-68542-closure-in-array-len.stderr
+++ b/tests/ui/consts/issue-68542-closure-in-array-len.stderr
@@ -6,7 +6,10 @@ LL |     a: [(); (|| { 0 })()]
    |
    = note: closures need an RFC before allowed to be called in constants
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/issue-73976-monomorphic.stderr b/tests/ui/consts/issue-73976-monomorphic.stderr
index 465efc7bfc2..79dbed4bea8 100644
--- a/tests/ui/consts/issue-73976-monomorphic.stderr
+++ b/tests/ui/consts/issue-73976-monomorphic.stderr
@@ -7,7 +7,10 @@ LL |     GetTypeId::::VALUE == GetTypeId::::VALUE
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/any.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/issue-90870.fixed b/tests/ui/consts/issue-90870.fixed
deleted file mode 100644
index c125501f61c..00000000000
--- a/tests/ui/consts/issue-90870.fixed
+++ /dev/null
@@ -1,37 +0,0 @@
-// Regression test for issue #90870.
-
-//@ run-rustfix
-
-#![allow(dead_code)]
-
-const fn f(a: &u8, b: &u8) -> bool {
-    *a == *b
-    //~^ ERROR: cannot call non-const operator in constant functions [E0015]
-    //~| HELP: consider dereferencing here
-    //~| HELP: add `#![feature(const_trait_impl)]`
-}
-
-const fn g(a: &&&&i64, b: &&&&i64) -> bool {
-    ****a == ****b
-    //~^ ERROR: cannot call non-const operator in constant functions [E0015]
-    //~| HELP: consider dereferencing here
-    //~| HELP: add `#![feature(const_trait_impl)]`
-}
-
-const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
-    while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
-        if *l == *r {
-        //~^ ERROR: cannot call non-const operator in constant functions [E0015]
-        //~| HELP: consider dereferencing here
-        //~| HELP: add `#![feature(const_trait_impl)]`
-            a = at;
-            b = bt;
-        } else {
-            return false;
-        }
-    }
-
-    a.is_empty() && b.is_empty()
-}
-
-fn main() {}
diff --git a/tests/ui/consts/issue-90870.rs b/tests/ui/consts/issue-90870.rs
index 94254fb27f9..e1929c68c70 100644
--- a/tests/ui/consts/issue-90870.rs
+++ b/tests/ui/consts/issue-90870.rs
@@ -1,21 +1,20 @@
 // Regression test for issue #90870.
 
-//@ run-rustfix
-
 #![allow(dead_code)]
 
 const fn f(a: &u8, b: &u8) -> bool {
+//~^ HELP: add `#![feature(const_trait_impl)]`
+//~| HELP: add `#![feature(const_trait_impl)]`
+//~| HELP: add `#![feature(const_trait_impl)]`
     a == b
     //~^ ERROR: cannot call non-const operator in constant functions [E0015]
     //~| HELP: consider dereferencing here
-    //~| HELP: add `#![feature(const_trait_impl)]`
 }
 
 const fn g(a: &&&&i64, b: &&&&i64) -> bool {
     a == b
     //~^ ERROR: cannot call non-const operator in constant functions [E0015]
     //~| HELP: consider dereferencing here
-    //~| HELP: add `#![feature(const_trait_impl)]`
 }
 
 const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
@@ -23,7 +22,6 @@ const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
         if l == r {
         //~^ ERROR: cannot call non-const operator in constant functions [E0015]
         //~| HELP: consider dereferencing here
-        //~| HELP: add `#![feature(const_trait_impl)]`
             a = at;
             b = bt;
         } else {
diff --git a/tests/ui/consts/issue-90870.stderr b/tests/ui/consts/issue-90870.stderr
index 8825efd1449..df88a0c95cc 100644
--- a/tests/ui/consts/issue-90870.stderr
+++ b/tests/ui/consts/issue-90870.stderr
@@ -1,15 +1,18 @@
 error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/issue-90870.rs:8:5
+  --> $DIR/issue-90870.rs:9:5
    |
 LL |     a == b
    |     ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 help: consider dereferencing here
    |
 LL |     *a == *b
    |     +     +
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const operator in constant functions
   --> $DIR/issue-90870.rs:15:5
@@ -18,24 +21,30 @@ LL |     a == b
    |     ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 help: consider dereferencing here
    |
 LL |     ****a == ****b
    |     ++++     ++++
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/issue-90870.rs:23:12
+  --> $DIR/issue-90870.rs:22:12
    |
 LL |         if l == r {
    |            ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 help: consider dereferencing here
    |
 LL |         if *l == *r {
    |            +     +
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr
index 60a56f85c11..ebfa09b2e5d 100644
--- a/tests/ui/consts/issue-94675.stderr
+++ b/tests/ui/consts/issue-94675.stderr
@@ -15,7 +15,10 @@ LL |         self.bar[0] = baz.len();
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/try-operator.stderr b/tests/ui/consts/try-operator.stderr
index c19d1a6199d..2c8b4c7fcd9 100644
--- a/tests/ui/consts/try-operator.stderr
+++ b/tests/ui/consts/try-operator.stderr
@@ -13,7 +13,10 @@ LL |         Err(())?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/result.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: `?` cannot convert from residual of `Result` in constant functions
   --> $DIR/try-operator.rs:10:9
@@ -24,7 +27,10 @@ LL |         Err(())?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/result.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: `?` cannot determine the branch of `Option<()>` in constant functions
   --> $DIR/try-operator.rs:18:9
@@ -35,7 +41,10 @@ LL |         None?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: `?` cannot convert from residual of `Option<()>` in constant functions
   --> $DIR/try-operator.rs:18:9
@@ -46,7 +55,10 @@ LL |         None?;
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/consts/unstable-const-fn-in-libcore.stderr b/tests/ui/consts/unstable-const-fn-in-libcore.stderr
index ee4a0f6a843..9590b3372e3 100644
--- a/tests/ui/consts/unstable-const-fn-in-libcore.stderr
+++ b/tests/ui/consts/unstable-const-fn-in-libcore.stderr
@@ -11,11 +11,14 @@ LL |             Opt::None => f(),
    |                          ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL |     const fn unwrap_or_else T + ~const std::ops::FnOnce<()>>(self, f: F) -> T {
    |                                                     +++++++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0493]: destructor of `F` cannot be evaluated at compile-time
   --> $DIR/unstable-const-fn-in-libcore.rs:19:60
diff --git a/tests/ui/cross/cross-fn-cache-hole.stderr b/tests/ui/cross/cross-fn-cache-hole.stderr
index dec2f2553c2..b7bcbc8933f 100644
--- a/tests/ui/cross/cross-fn-cache-hole.stderr
+++ b/tests/ui/cross/cross-fn-cache-hole.stderr
@@ -10,7 +10,10 @@ help: this trait has no implementations, consider adding one
 LL | trait Bar { }
    | ^^^^^^^^^^^^
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `i32: Bar` is not satisfied
   --> $DIR/cross-fn-cache-hole.rs:30:15
diff --git a/tests/ui/entry-point/imported_main_conflict.rs b/tests/ui/entry-point/imported_main_conflict.rs
index e8c70b06513..06178dbeff7 100644
--- a/tests/ui/entry-point/imported_main_conflict.rs
+++ b/tests/ui/entry-point/imported_main_conflict.rs
@@ -1,5 +1,4 @@
-#![feature(imported_main)]
-//~^ ERROR `main` is ambiguous
+//~ ERROR `main` is ambiguous
 mod m1 { pub(crate) fn main() {} }
 mod m2 { pub(crate) fn main() {} }
 
diff --git a/tests/ui/entry-point/imported_main_conflict.stderr b/tests/ui/entry-point/imported_main_conflict.stderr
index 783e9345acf..3ef34962524 100644
--- a/tests/ui/entry-point/imported_main_conflict.stderr
+++ b/tests/ui/entry-point/imported_main_conflict.stderr
@@ -2,13 +2,13 @@ error[E0659]: `main` is ambiguous
    |
    = note: ambiguous because of multiple glob imports of a name in the same module
 note: `main` could refer to the function imported here
-  --> $DIR/imported_main_conflict.rs:6:5
+  --> $DIR/imported_main_conflict.rs:5:5
    |
 LL | use m1::*;
    |     ^^^^^
    = help: consider adding an explicit import of `main` to disambiguate
 note: `main` could also refer to the function imported here
-  --> $DIR/imported_main_conflict.rs:7:5
+  --> $DIR/imported_main_conflict.rs:6:5
    |
 LL | use m2::*;
    |     ^^^^^
diff --git a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs
index 405d6e2a9f5..d0be240e07b 100644
--- a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs
+++ b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.rs
@@ -1,4 +1,3 @@
-#![feature(imported_main)]
 #![feature(type_alias_impl_trait)]
 #![allow(incomplete_features)]
 pub mod foo {
diff --git a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr
index 1e7d82a73bc..50e4cd7d39f 100644
--- a/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr
+++ b/tests/ui/entry-point/imported_main_const_fn_item_type_forbidden.stderr
@@ -1,5 +1,5 @@
 error[E0601]: `main` function not found in crate `imported_main_const_fn_item_type_forbidden`
-  --> $DIR/imported_main_const_fn_item_type_forbidden.rs:11:22
+  --> $DIR/imported_main_const_fn_item_type_forbidden.rs:10:22
    |
 LL | use foo::BAR as main;
    |     ---------------- ^ consider adding a `main` function to `$DIR/imported_main_const_fn_item_type_forbidden.rs`
diff --git a/tests/ui/entry-point/imported_main_const_forbidden.rs b/tests/ui/entry-point/imported_main_const_forbidden.rs
index 1508280c0fa..c478e004603 100644
--- a/tests/ui/entry-point/imported_main_const_forbidden.rs
+++ b/tests/ui/entry-point/imported_main_const_forbidden.rs
@@ -1,4 +1,3 @@
-#![feature(imported_main)]
 pub mod foo {
     pub const BAR: usize = 42;
 }
diff --git a/tests/ui/entry-point/imported_main_const_forbidden.stderr b/tests/ui/entry-point/imported_main_const_forbidden.stderr
index 6f34015a2cd..eb768b1b250 100644
--- a/tests/ui/entry-point/imported_main_const_forbidden.stderr
+++ b/tests/ui/entry-point/imported_main_const_forbidden.stderr
@@ -1,5 +1,5 @@
 error[E0601]: `main` function not found in crate `imported_main_const_forbidden`
-  --> $DIR/imported_main_const_forbidden.rs:6:22
+  --> $DIR/imported_main_const_forbidden.rs:5:22
    |
 LL | use foo::BAR as main;
    |     ---------------- ^ consider adding a `main` function to `$DIR/imported_main_const_forbidden.rs`
diff --git a/tests/ui/entry-point/imported_main_from_extern_crate.rs b/tests/ui/entry-point/imported_main_from_extern_crate.rs
index abcf2cbc05b..6d1c497052e 100644
--- a/tests/ui/entry-point/imported_main_from_extern_crate.rs
+++ b/tests/ui/entry-point/imported_main_from_extern_crate.rs
@@ -1,7 +1,5 @@
 //@ run-pass
 //@ aux-build:main_functions.rs
 
-#![feature(imported_main)]
-
 extern crate main_functions;
 pub use main_functions::boilerplate as main;
diff --git a/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs b/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs
index 82d81a93d9d..d8ae12d200f 100644
--- a/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs
+++ b/tests/ui/entry-point/imported_main_from_extern_crate_wrong_type.rs
@@ -1,6 +1,4 @@
 //@ aux-build:bad_main_functions.rs
 
-#![feature(imported_main)]
-
 extern crate bad_main_functions;
 pub use bad_main_functions::boilerplate as main;
diff --git a/tests/ui/entry-point/imported_main_from_inner_mod.rs b/tests/ui/entry-point/imported_main_from_inner_mod.rs
index 7212dd6182c..cede1766118 100644
--- a/tests/ui/entry-point/imported_main_from_inner_mod.rs
+++ b/tests/ui/entry-point/imported_main_from_inner_mod.rs
@@ -1,5 +1,4 @@
 //@ run-pass
-#![feature(imported_main)]
 
 pub mod foo {
     pub fn bar() {
diff --git a/tests/ui/extenv/extenv-arg-2-not-string-literal.rs b/tests/ui/env-macro/env-arg-2-not-string-literal.rs
similarity index 100%
rename from tests/ui/extenv/extenv-arg-2-not-string-literal.rs
rename to tests/ui/env-macro/env-arg-2-not-string-literal.rs
diff --git a/tests/ui/extenv/extenv-arg-2-not-string-literal.stderr b/tests/ui/env-macro/env-arg-2-not-string-literal.stderr
similarity index 74%
rename from tests/ui/extenv/extenv-arg-2-not-string-literal.stderr
rename to tests/ui/env-macro/env-arg-2-not-string-literal.stderr
index 9db1c0be746..843ce4823ea 100644
--- a/tests/ui/extenv/extenv-arg-2-not-string-literal.stderr
+++ b/tests/ui/env-macro/env-arg-2-not-string-literal.stderr
@@ -1,5 +1,5 @@
 error: expected string literal
-  --> $DIR/extenv-arg-2-not-string-literal.rs:1:25
+  --> $DIR/env-arg-2-not-string-literal.rs:1:25
    |
 LL | fn main() { env!("one", 10); }
    |                         ^^
diff --git a/tests/ui/extenv/extenv-env-overload.rs b/tests/ui/env-macro/env-env-overload.rs
similarity index 100%
rename from tests/ui/extenv/extenv-env-overload.rs
rename to tests/ui/env-macro/env-env-overload.rs
diff --git a/tests/ui/extenv/extenv-env.rs b/tests/ui/env-macro/env-env.rs
similarity index 100%
rename from tests/ui/extenv/extenv-env.rs
rename to tests/ui/env-macro/env-env.rs
diff --git a/tests/ui/extenv/extenv-escaped-var.rs b/tests/ui/env-macro/env-escaped-var.rs
similarity index 100%
rename from tests/ui/extenv/extenv-escaped-var.rs
rename to tests/ui/env-macro/env-escaped-var.rs
diff --git a/tests/ui/extenv/extenv-escaped-var.stderr b/tests/ui/env-macro/env-escaped-var.stderr
similarity index 90%
rename from tests/ui/extenv/extenv-escaped-var.stderr
rename to tests/ui/env-macro/env-escaped-var.stderr
index ef5e654d054..53a2ead6742 100644
--- a/tests/ui/extenv/extenv-escaped-var.stderr
+++ b/tests/ui/env-macro/env-escaped-var.stderr
@@ -1,5 +1,5 @@
 error: environment variable `\t` not defined at compile time
-  --> $DIR/extenv-escaped-var.rs:2:5
+  --> $DIR/env-escaped-var.rs:2:5
    |
 LL |     env!("\t");
    |     ^^^^^^^^^^
diff --git a/tests/ui/extenv/extenv-no-args.rs b/tests/ui/env-macro/env-no-args.rs
similarity index 100%
rename from tests/ui/extenv/extenv-no-args.rs
rename to tests/ui/env-macro/env-no-args.rs
diff --git a/tests/ui/extenv/extenv-no-args.stderr b/tests/ui/env-macro/env-no-args.stderr
similarity index 80%
rename from tests/ui/extenv/extenv-no-args.stderr
rename to tests/ui/env-macro/env-no-args.stderr
index 36d485676c2..3a94b36cd0f 100644
--- a/tests/ui/extenv/extenv-no-args.stderr
+++ b/tests/ui/env-macro/env-no-args.stderr
@@ -1,5 +1,5 @@
 error: `env!()` takes 1 or 2 arguments
-  --> $DIR/extenv-no-args.rs:1:13
+  --> $DIR/env-no-args.rs:1:13
    |
 LL | fn main() { env!(); }
    |             ^^^^^^
diff --git a/tests/ui/extenv/extenv-not-defined-custom.rs b/tests/ui/env-macro/env-not-defined-custom.rs
similarity index 100%
rename from tests/ui/extenv/extenv-not-defined-custom.rs
rename to tests/ui/env-macro/env-not-defined-custom.rs
diff --git a/tests/ui/extenv/extenv-not-defined-custom.stderr b/tests/ui/env-macro/env-not-defined-custom.stderr
similarity index 88%
rename from tests/ui/extenv/extenv-not-defined-custom.stderr
rename to tests/ui/env-macro/env-not-defined-custom.stderr
index 9b6e32bc95f..70c41bcc52e 100644
--- a/tests/ui/extenv/extenv-not-defined-custom.stderr
+++ b/tests/ui/env-macro/env-not-defined-custom.stderr
@@ -1,5 +1,5 @@
 error: my error message
-  --> $DIR/extenv-not-defined-custom.rs:1:13
+  --> $DIR/env-not-defined-custom.rs:1:13
    |
 LL | fn main() { env!("__HOPEFULLY_NOT_DEFINED__", "my error message"); }
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extenv/extenv-not-defined-default.rs b/tests/ui/env-macro/env-not-defined-default.rs
similarity index 100%
rename from tests/ui/extenv/extenv-not-defined-default.rs
rename to tests/ui/env-macro/env-not-defined-default.rs
diff --git a/tests/ui/extenv/extenv-not-defined-default.stderr b/tests/ui/env-macro/env-not-defined-default.stderr
similarity index 91%
rename from tests/ui/extenv/extenv-not-defined-default.stderr
rename to tests/ui/env-macro/env-not-defined-default.stderr
index 5198818f89c..efd7fdb4e53 100644
--- a/tests/ui/extenv/extenv-not-defined-default.stderr
+++ b/tests/ui/env-macro/env-not-defined-default.stderr
@@ -1,5 +1,5 @@
 error: environment variable `CARGO__HOPEFULLY_NOT_DEFINED__` not defined at compile time
-  --> $DIR/extenv-not-defined-default.rs:2:5
+  --> $DIR/env-not-defined-default.rs:2:5
    |
 LL |     env!("CARGO__HOPEFULLY_NOT_DEFINED__");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extenv/extenv-not-env.rs b/tests/ui/env-macro/env-not-env.rs
similarity index 100%
rename from tests/ui/extenv/extenv-not-env.rs
rename to tests/ui/env-macro/env-not-env.rs
diff --git a/tests/ui/extenv/extenv-not-string-literal.rs b/tests/ui/env-macro/env-not-string-literal.rs
similarity index 100%
rename from tests/ui/extenv/extenv-not-string-literal.rs
rename to tests/ui/env-macro/env-not-string-literal.rs
diff --git a/tests/ui/extenv/extenv-not-string-literal.stderr b/tests/ui/env-macro/env-not-string-literal.stderr
similarity index 75%
rename from tests/ui/extenv/extenv-not-string-literal.stderr
rename to tests/ui/env-macro/env-not-string-literal.stderr
index 85ed442e2fe..0985459eff9 100644
--- a/tests/ui/extenv/extenv-not-string-literal.stderr
+++ b/tests/ui/env-macro/env-not-string-literal.stderr
@@ -1,5 +1,5 @@
 error: expected string literal
-  --> $DIR/extenv-not-string-literal.rs:1:18
+  --> $DIR/env-not-string-literal.rs:1:18
    |
 LL | fn main() { env!(10, "two"); }
    |                  ^^
diff --git a/tests/ui/extenv/extenv-too-many-args.rs b/tests/ui/env-macro/env-too-many-args.rs
similarity index 100%
rename from tests/ui/extenv/extenv-too-many-args.rs
rename to tests/ui/env-macro/env-too-many-args.rs
diff --git a/tests/ui/extenv/extenv-too-many-args.stderr b/tests/ui/env-macro/env-too-many-args.stderr
similarity index 81%
rename from tests/ui/extenv/extenv-too-many-args.stderr
rename to tests/ui/env-macro/env-too-many-args.stderr
index c0fd5d57251..f156026846a 100644
--- a/tests/ui/extenv/extenv-too-many-args.stderr
+++ b/tests/ui/env-macro/env-too-many-args.stderr
@@ -1,5 +1,5 @@
 error: `env!()` takes 1 or 2 arguments
-  --> $DIR/extenv-too-many-args.rs:1:13
+  --> $DIR/env-too-many-args.rs:1:13
    |
 LL | fn main() { env!("one", "two", "three"); }
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extenv/issue-55897.rs b/tests/ui/env-macro/error-recovery-issue-55897.rs
similarity index 100%
rename from tests/ui/extenv/issue-55897.rs
rename to tests/ui/env-macro/error-recovery-issue-55897.rs
diff --git a/tests/ui/extenv/issue-55897.stderr b/tests/ui/env-macro/error-recovery-issue-55897.stderr
similarity index 85%
rename from tests/ui/extenv/issue-55897.stderr
rename to tests/ui/env-macro/error-recovery-issue-55897.stderr
index 2e8c05cca86..5a20bf8b168 100644
--- a/tests/ui/extenv/issue-55897.stderr
+++ b/tests/ui/env-macro/error-recovery-issue-55897.stderr
@@ -1,5 +1,5 @@
 error: environment variable `NON_EXISTENT` not defined at compile time
-  --> $DIR/issue-55897.rs:10:22
+  --> $DIR/error-recovery-issue-55897.rs:10:22
    |
 LL |     include!(concat!(env!("NON_EXISTENT"), "/data.rs"));
    |                      ^^^^^^^^^^^^^^^^^^^^
@@ -8,13 +8,13 @@ LL |     include!(concat!(env!("NON_EXISTENT"), "/data.rs"));
    = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: suffixes on string literals are invalid
-  --> $DIR/issue-55897.rs:15:22
+  --> $DIR/error-recovery-issue-55897.rs:15:22
    |
 LL |     include!(concat!("NON_EXISTENT"suffix, "/data.rs"));
    |                      ^^^^^^^^^^^^^^^^^^^^ invalid suffix `suffix`
 
 error[E0432]: unresolved import `prelude`
-  --> $DIR/issue-55897.rs:1:5
+  --> $DIR/error-recovery-issue-55897.rs:1:5
    |
 LL | use prelude::*;
    |     ^^^^^^^
@@ -23,7 +23,7 @@ LL | use prelude::*;
    |     help: a similar path exists: `std::prelude`
 
 error[E0432]: unresolved import `env`
-  --> $DIR/issue-55897.rs:4:9
+  --> $DIR/error-recovery-issue-55897.rs:4:9
    |
 LL |     use env;
    |         ^^^ no `env` in the root
diff --git a/tests/ui/extenv/issue-110547.rs b/tests/ui/env-macro/name-whitespace-issue-110547.rs
similarity index 100%
rename from tests/ui/extenv/issue-110547.rs
rename to tests/ui/env-macro/name-whitespace-issue-110547.rs
diff --git a/tests/ui/extenv/issue-110547.stderr b/tests/ui/env-macro/name-whitespace-issue-110547.stderr
similarity index 87%
rename from tests/ui/extenv/issue-110547.stderr
rename to tests/ui/env-macro/name-whitespace-issue-110547.stderr
index 10589ec2f54..5f34904d4ae 100644
--- a/tests/ui/extenv/issue-110547.stderr
+++ b/tests/ui/env-macro/name-whitespace-issue-110547.stderr
@@ -1,5 +1,5 @@
 error: environment variable `\t` not defined at compile time
-  --> $DIR/issue-110547.rs:4:5
+  --> $DIR/name-whitespace-issue-110547.rs:4:5
    |
 LL |     env!{"\t"};
    |     ^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     env!{"\t"};
    = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: environment variable `\t` not defined at compile time
-  --> $DIR/issue-110547.rs:5:5
+  --> $DIR/name-whitespace-issue-110547.rs:5:5
    |
 LL |     env!("\t");
    |     ^^^^^^^^^^
@@ -17,7 +17,7 @@ LL |     env!("\t");
    = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: environment variable `\u{2069}` not defined at compile time
-  --> $DIR/issue-110547.rs:6:5
+  --> $DIR/name-whitespace-issue-110547.rs:6:5
    |
 LL |     env!("\u{2069}");
    |     ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/extoption_env-no-args.rs b/tests/ui/env-macro/option_env-no-args.rs
similarity index 100%
rename from tests/ui/extoption_env-no-args.rs
rename to tests/ui/env-macro/option_env-no-args.rs
diff --git a/tests/ui/extoption_env-no-args.stderr b/tests/ui/env-macro/option_env-no-args.stderr
similarity index 78%
rename from tests/ui/extoption_env-no-args.stderr
rename to tests/ui/env-macro/option_env-no-args.stderr
index d40f905b665..d621aff770d 100644
--- a/tests/ui/extoption_env-no-args.stderr
+++ b/tests/ui/env-macro/option_env-no-args.stderr
@@ -1,5 +1,5 @@
 error: option_env! takes 1 argument
-  --> $DIR/extoption_env-no-args.rs:1:13
+  --> $DIR/option_env-no-args.rs:1:13
    |
 LL | fn main() { option_env!(); }
    |             ^^^^^^^^^^^^^
diff --git a/tests/ui/extoption_env-not-defined.rs b/tests/ui/env-macro/option_env-not-defined.rs
similarity index 100%
rename from tests/ui/extoption_env-not-defined.rs
rename to tests/ui/env-macro/option_env-not-defined.rs
diff --git a/tests/ui/extoption_env-not-string-literal.rs b/tests/ui/env-macro/option_env-not-string-literal.rs
similarity index 100%
rename from tests/ui/extoption_env-not-string-literal.rs
rename to tests/ui/env-macro/option_env-not-string-literal.rs
diff --git a/tests/ui/extoption_env-not-string-literal.stderr b/tests/ui/env-macro/option_env-not-string-literal.stderr
similarity index 75%
rename from tests/ui/extoption_env-not-string-literal.stderr
rename to tests/ui/env-macro/option_env-not-string-literal.stderr
index d4fec1b45c9..3d2542a0e6c 100644
--- a/tests/ui/extoption_env-not-string-literal.stderr
+++ b/tests/ui/env-macro/option_env-not-string-literal.stderr
@@ -1,5 +1,5 @@
 error: argument must be a string literal
-  --> $DIR/extoption_env-not-string-literal.rs:1:25
+  --> $DIR/option_env-not-string-literal.rs:1:25
    |
 LL | fn main() { option_env!(10); }
    |                         ^^
diff --git a/tests/ui/extoption_env-too-many-args.rs b/tests/ui/env-macro/option_env-too-many-args.rs
similarity index 100%
rename from tests/ui/extoption_env-too-many-args.rs
rename to tests/ui/env-macro/option_env-too-many-args.rs
diff --git a/tests/ui/extoption_env-too-many-args.stderr b/tests/ui/env-macro/option_env-too-many-args.stderr
similarity index 78%
rename from tests/ui/extoption_env-too-many-args.stderr
rename to tests/ui/env-macro/option_env-too-many-args.stderr
index c7aeaac75dd..b4da3670787 100644
--- a/tests/ui/extoption_env-too-many-args.stderr
+++ b/tests/ui/env-macro/option_env-too-many-args.stderr
@@ -1,5 +1,5 @@
 error: option_env! takes 1 argument
-  --> $DIR/extoption_env-too-many-args.rs:1:13
+  --> $DIR/option_env-too-many-args.rs:1:13
    |
 LL | fn main() { option_env!("one", "two"); }
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr
index e6eeca2e098..fcb9b8a6fc5 100644
--- a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr
+++ b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr
@@ -5,7 +5,10 @@ LL | struct Foo;
    |                        ^^^^^^^^^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs b/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs
deleted file mode 100644
index 717da41f871..00000000000
--- a/tests/ui/feature-gates/feature-gate-associated_type_bounds.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use std::mem::ManuallyDrop;
-
-trait Tr1 { type As1: Copy; }
-trait Tr2 { type As2: Copy; }
-
-struct S1;
-#[derive(Copy, Clone)]
-struct S2;
-impl Tr1 for S1 { type As1 = S2; }
-
-trait _Tr3 {
-    type A: Iterator;
-    //~^ ERROR associated type bounds are unstable
-
-    type B: Iterator;
-    //~^ ERROR associated type bounds are unstable
-}
-
-struct _St1> {
-//~^ ERROR associated type bounds are unstable
-    outest: T,
-    outer: T::As1,
-    inner: ::As2,
-}
-
-enum _En1> {
-//~^ ERROR associated type bounds are unstable
-    Outest(T),
-    Outer(T::As1),
-    Inner(::As2),
-}
-
-union _Un1> {
-//~^ ERROR associated type bounds are unstable
-    outest: ManuallyDrop,
-    outer: ManuallyDrop,
-    inner: ManuallyDrop<::As2>,
-}
-
-type _TaWhere1 where T: Iterator = T;
-//~^ ERROR associated type bounds are unstable
-
-fn _apit(_: impl Tr1) {}
-//~^ ERROR associated type bounds are unstable
-
-fn _rpit() -> impl Tr1 { S1 }
-//~^ ERROR associated type bounds are unstable
-
-const _cdef: impl Tr1 = S1;
-//~^ ERROR associated type bounds are unstable
-//~| ERROR `impl Trait` is not allowed in const types
-
-static _sdef: impl Tr1 = S1;
-//~^ ERROR associated type bounds are unstable
-//~| ERROR `impl Trait` is not allowed in static types
-
-fn main() {
-    let _: impl Tr1 = S1;
-    //~^ ERROR associated type bounds are unstable
-    //~| ERROR `impl Trait` is not allowed in the type of variable bindings
-}
diff --git a/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr b/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr
deleted file mode 100644
index 1838eab5cda..00000000000
--- a/tests/ui/feature-gates/feature-gate-associated_type_bounds.stderr
+++ /dev/null
@@ -1,138 +0,0 @@
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:12:22
-   |
-LL |     type A: Iterator;
-   |                      ^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:15:22
-   |
-LL |     type B: Iterator;
-   |                      ^^^^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:19:20
-   |
-LL | struct _St1> {
-   |                    ^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:26:18
-   |
-LL | enum _En1> {
-   |                  ^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:33:19
-   |
-LL | union _Un1> {
-   |                   ^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:40:37
-   |
-LL | type _TaWhere1 where T: Iterator = T;
-   |                                     ^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:43:22
-   |
-LL | fn _apit(_: impl Tr1) {}
-   |                      ^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:46:24
-   |
-LL | fn _rpit() -> impl Tr1 { S1 }
-   |                        ^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:49:23
-   |
-LL | const _cdef: impl Tr1 = S1;
-   |                       ^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:53:24
-   |
-LL | static _sdef: impl Tr1 = S1;
-   |                        ^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: associated type bounds are unstable
-  --> $DIR/feature-gate-associated_type_bounds.rs:58:21
-   |
-LL |     let _: impl Tr1 = S1;
-   |                     ^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0562]: `impl Trait` is not allowed in const types
-  --> $DIR/feature-gate-associated_type_bounds.rs:49:14
-   |
-LL | const _cdef: impl Tr1 = S1;
-   |              ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
-
-error[E0562]: `impl Trait` is not allowed in static types
-  --> $DIR/feature-gate-associated_type_bounds.rs:53:15
-   |
-LL | static _sdef: impl Tr1 = S1;
-   |               ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
-
-error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/feature-gate-associated_type_bounds.rs:58:12
-   |
-LL |     let _: impl Tr1 = S1;
-   |            ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
-
-error: aborting due to 14 previous errors
-
-Some errors have detailed explanations: E0562, E0658.
-For more information about an error, try `rustc --explain E0562`.
diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
index bc022476c19..97370f0489b 100644
--- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
+++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
@@ -27,7 +27,10 @@ LL |     let _x = foo::<_>([1,2]);
    |                    ^
    |
    = help: const arguments cannot yet be inferred with `_`
-   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+   |
+LL + #![feature(generic_arg_infer)]
+   |
 
 error[E0658]: using `_` for array lengths is unstable
   --> $DIR/feature-gate-generic_arg_infer.rs:11:27
diff --git a/tests/ui/feature-gates/feature-gate-imported_main.rs b/tests/ui/feature-gates/feature-gate-imported_main.rs
deleted file mode 100644
index b351d0d0e9a..00000000000
--- a/tests/ui/feature-gates/feature-gate-imported_main.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod foo {
-    pub fn bar() {
-        println!("Hello world!");
-    }
-}
-use foo::bar as main; //~ ERROR using an imported function as entry point
diff --git a/tests/ui/feature-gates/feature-gate-imported_main.stderr b/tests/ui/feature-gates/feature-gate-imported_main.stderr
deleted file mode 100644
index 987bda7059c..00000000000
--- a/tests/ui/feature-gates/feature-gate-imported_main.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-error[E0658]: using an imported function as entry point `main` is experimental
-  --> $DIR/feature-gate-imported_main.rs:6:5
-   |
-LL | use foo::bar as main;
-   |     ^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #28937  for more information
-   = help: add `#![feature(imported_main)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr b/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr
index 5e62221628d..0ee2d93fb26 100644
--- a/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr
+++ b/tests/ui/feature-gates/feature-gate-trivial_bounds.stderr
@@ -6,7 +6,10 @@ LL | enum E where i32: Foo { V }
    |
    = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/feature-gate-trivial_bounds.rs:12:16
@@ -16,7 +19,10 @@ LL | struct S where i32: Foo;
    |
    = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/feature-gate-trivial_bounds.rs:14:15
@@ -26,7 +32,10 @@ LL | trait T where i32: Foo {}
    |
    = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/feature-gate-trivial_bounds.rs:16:15
@@ -36,7 +45,10 @@ LL | union U where i32: Foo { f: i32 }
    |
    = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/feature-gate-trivial_bounds.rs:20:23
@@ -46,7 +58,10 @@ LL | impl Foo for () where i32: Foo {
    |
    = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
   --> $DIR/feature-gate-trivial_bounds.rs:28:14
@@ -56,7 +71,10 @@ LL | fn f() where i32: Foo
    |
    = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `String: Neg` is not satisfied
   --> $DIR/feature-gate-trivial_bounds.rs:36:38
@@ -65,7 +83,10 @@ LL | fn use_op(s: String) -> String where String: ::std::ops::Neg
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Neg` is not implemented for `String`
    |
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: `i32` is not an iterator
   --> $DIR/feature-gate-trivial_bounds.rs:40:20
@@ -75,7 +96,10 @@ LL | fn use_for() where i32: Iterator {
    |
    = help: the trait `Iterator` is not implemented for `i32`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
   --> $DIR/feature-gate-trivial_bounds.rs:52:32
@@ -85,7 +109,10 @@ LL | struct TwoStrs(str, str) where str: Sized;
    |
    = help: the trait `Sized` is not implemented for `str`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time
   --> $DIR/feature-gate-trivial_bounds.rs:55:26
@@ -100,7 +127,10 @@ note: required because it appears within the type `Dst<(dyn A + 'static)>`
 LL | struct Dst {
    |        ^^^
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
   --> $DIR/feature-gate-trivial_bounds.rs:59:30
@@ -110,7 +140,10 @@ LL | fn return_str() -> str where str: Sized {
    |
    = help: the trait `Sized` is not implemented for `str`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/fmt/nested-awaits-issue-122674.rs b/tests/ui/fmt/nested-awaits-issue-122674.rs
new file mode 100644
index 00000000000..f250933dadb
--- /dev/null
+++ b/tests/ui/fmt/nested-awaits-issue-122674.rs
@@ -0,0 +1,22 @@
+// Non-regression test for issue #122674: a change in the format args visitor missed nested awaits.
+
+//@ edition: 2021
+//@ check-pass
+
+pub fn f1() -> impl std::future::Future> + Send {
+    async {
+        should_work().await?;
+        Ok(())
+    }
+}
+
+async fn should_work() -> Result {
+    let x = 1;
+    Err(format!("test: {}: {}", x, inner().await?))
+}
+
+async fn inner() -> Result {
+    Ok("test".to_string())
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/elided-lifetimes.stderr b/tests/ui/generic-const-items/elided-lifetimes.stderr
index e7df8ca5cfd..1d4a997ff60 100644
--- a/tests/ui/generic-const-items/elided-lifetimes.stderr
+++ b/tests/ui/generic-const-items/elided-lifetimes.stderr
@@ -28,7 +28,10 @@ LL | const I: &str = "";
    |                  ^^^^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/impl-trait/in-trait/lifetime-in-associated-trait-bound.rs b/tests/ui/impl-trait/in-trait/lifetime-in-associated-trait-bound.rs
index e0d4f461974..b29d71437b9 100644
--- a/tests/ui/impl-trait/in-trait/lifetime-in-associated-trait-bound.rs
+++ b/tests/ui/impl-trait/in-trait/lifetime-in-associated-trait-bound.rs
@@ -1,7 +1,5 @@
 //@ check-pass
 
-#![feature(associated_type_bounds)]
-
 trait Trait {
     type Type;
 
diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr
index f77b4bd517f..7ae8306d74d 100644
--- a/tests/ui/impl-trait/normalize-tait-in-const.stderr
+++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr
@@ -11,11 +11,14 @@ LL |     fun(filter_positive());
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct + ~const std::ops::Fn<(&Alias<'_>,)>>(fun: F) {
    |                                                                              ++++++++++++++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0493]: destructor of `F` cannot be evaluated at compile-time
   --> $DIR/normalize-tait-in-const.rs:25:79
diff --git a/tests/ui/inference/inference_unstable.stderr b/tests/ui/inference/inference_unstable.stderr
index c48aaf9f495..51f086177db 100644
--- a/tests/ui/inference/inference_unstable.stderr
+++ b/tests/ui/inference/inference_unstable.stderr
@@ -7,8 +7,11 @@ LL |     assert_eq!('x'.ipu_flatten(), 1);
    = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 
    = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method
-   = help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten`
    = note: `#[warn(unstable_name_collisions)]` on by default
+help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten`
+   |
+LL + #![feature(ipu_flatten)]
+   |
 
 warning: a method with this name may be added to the standard library in the future
   --> $DIR/inference_unstable.rs:19:20
@@ -19,7 +22,10 @@ LL |     assert_eq!('x'.ipu_by_value_vs_by_ref(), 1);
    = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 
    = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_value_vs_by_ref(...)` to keep using the current method
-   = help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_value_vs_by_ref`
+help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_value_vs_by_ref`
+   |
+LL + #![feature(ipu_flatten)]
+   |
 
 warning: a method with this name may be added to the standard library in the future
   --> $DIR/inference_unstable.rs:22:20
@@ -30,7 +36,10 @@ LL |     assert_eq!('x'.ipu_by_ref_vs_by_ref_mut(), 1);
    = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 
    = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_ref_vs_by_ref_mut(...)` to keep using the current method
-   = help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_ref_vs_by_ref_mut`
+help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_ref_vs_by_ref_mut`
+   |
+LL + #![feature(ipu_flatten)]
+   |
 
 warning: a method with this name may be added to the standard library in the future
   --> $DIR/inference_unstable.rs:25:40
@@ -41,17 +50,27 @@ LL |     assert_eq!((&mut 'x' as *mut char).ipu_by_mut_ptr_vs_by_const_ptr(), 1)
    = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 
    = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_mut_ptr_vs_by_const_ptr(...)` to keep using the current method
-   = help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_mut_ptr_vs_by_const_ptr`
+help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_mut_ptr_vs_by_const_ptr`
+   |
+LL + #![feature(ipu_flatten)]
+   |
 
 warning: an associated constant with this name may be added to the standard library in the future
   --> $DIR/inference_unstable.rs:28:16
    |
 LL |     assert_eq!(char::C, 1);
-   |                ^^^^^^^ help: use the fully qualified path to the associated const: `::C`
+   |                ^^^^^^^
    |
    = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior!
    = note: for more information, see issue #48919 
-   = help: add `#![feature(assoc_const_ipu_iter)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::C`
+help: use the fully qualified path to the associated const
+   |
+LL |     assert_eq!(::C, 1);
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: add `#![feature(assoc_const_ipu_iter)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::C`
+   |
+LL + #![feature(assoc_const_ipu_iter)]
+   |
 
 warning: 5 warnings emitted
 
diff --git a/tests/ui/issues/issue-25901.stderr b/tests/ui/issues/issue-25901.stderr
index 673f29fff18..5c19abffa02 100644
--- a/tests/ui/issues/issue-25901.stderr
+++ b/tests/ui/issues/issue-25901.stderr
@@ -16,8 +16,11 @@ note: impl defined here, but it is not `const`
 LL | impl Deref for A {
    | ^^^^^^^^^^^^^^^^
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr
index e3b70232ef8..320e64a2f77 100644
--- a/tests/ui/lifetimes/unusual-rib-combinations.stderr
+++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr
@@ -56,7 +56,10 @@ LL | fn d() {}
    |               ^
    |
    = note: the only supported types are integers, `bool` and `char`
-   = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
+   |
+LL + #![feature(adt_const_params)]
+   |
 
 error: aborting due to 8 previous errors
 
diff --git a/tests/ui/lint/decorate-ice/decorate-can-emit-warnings.rs b/tests/ui/lint/decorate-ice/decorate-can-emit-warnings.rs
new file mode 100644
index 00000000000..5adb5f526dc
--- /dev/null
+++ b/tests/ui/lint/decorate-ice/decorate-can-emit-warnings.rs
@@ -0,0 +1,11 @@
+// Checks that the following does not ICE because `decorate` is incorrectly skipped.
+
+//@ compile-flags: -Dunused_must_use -Awarnings --crate-type=lib
+
+#[must_use]
+fn f() {}
+
+pub fn g() {
+    f();
+    //~^ ERROR unused return value
+}
diff --git a/tests/ui/lint/decorate-ice/decorate-can-emit-warnings.stderr b/tests/ui/lint/decorate-ice/decorate-can-emit-warnings.stderr
new file mode 100644
index 00000000000..fde81de6136
--- /dev/null
+++ b/tests/ui/lint/decorate-ice/decorate-can-emit-warnings.stderr
@@ -0,0 +1,14 @@
+error: unused return value of `f` that must be used
+  --> $DIR/decorate-can-emit-warnings.rs:9:5
+   |
+LL |     f();
+   |     ^^^
+   |
+   = note: requested on the command line with `-D unused-must-use`
+help: use `let _ = ...` to ignore the resulting value
+   |
+LL |     let _ = f();
+   |     +++++++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs b/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs
new file mode 100644
index 00000000000..8116e36ed0d
--- /dev/null
+++ b/tests/ui/lint/decorate-ice/decorate-def-path-str-ice.rs
@@ -0,0 +1,30 @@
+// Checks that the following does not ICE.
+//
+// Previously, this test ICEs when the `unused_must_use` lint is suppressed via the combination of
+// `-A warnings` and `--cap-lints=warn`, because:
+//
+// - Its lint diagnostic struct `UnusedDef` implements `LintDiagnostic` manually and in the impl
+//   `def_path_str` was called (which calls `trimmed_def_path`, which will produce a
+//   `must_produce_diag` ICE if a trimmed def path is constructed but never emitted in a diagnostic
+//   because it is expensive to compute).
+// - A `LintDiagnostic` has a `decorate_lint` method which decorates a `Diag` with lint-specific
+//   information. This method is wrapped by a `decorate` closure in `TyCtxt` diagnostic emission
+//   machinery, and the `decorate` closure called as late as possible.
+// - `decorate`'s invocation is delayed as late as possible until `lint_level` is called.
+// - If a lint's corresponding diagnostic is suppressed (to be effectively allow at the final
+//   emission time) via `-A warnings` or `--cap-lints=allow` (or `-A warnings` + `--cap-lints=warn`
+//   like in this test case), `decorate` is still called and a diagnostic is still constructed --
+//   but the diagnostic is never eventually emitted, triggering the aforementioned
+//   `must_produce_diag` ICE due to use of `trimmed_def_path`.
+//
+// Issue: .
+
+//@ compile-flags: -Dunused_must_use -Awarnings --cap-lints=warn --crate-type=lib
+//@ check-pass
+
+#[must_use]
+fn f() {}
+
+pub fn g() {
+    f();
+}
diff --git a/tests/ui/lint/decorate-ice/decorate-force-warn.rs b/tests/ui/lint/decorate-ice/decorate-force-warn.rs
new file mode 100644
index 00000000000..e33210ed0ce
--- /dev/null
+++ b/tests/ui/lint/decorate-ice/decorate-force-warn.rs
@@ -0,0 +1,13 @@
+// Checks that the following does not ICE because `decorate` is incorrectly skipped due to
+// `--force-warn`.
+
+//@ compile-flags: -Dunused_must_use -Awarnings --force-warn unused_must_use --crate-type=lib
+//@ check-pass
+
+#[must_use]
+fn f() {}
+
+pub fn g() {
+    f();
+    //~^ WARN unused return value
+}
diff --git a/tests/ui/lint/decorate-ice/decorate-force-warn.stderr b/tests/ui/lint/decorate-ice/decorate-force-warn.stderr
new file mode 100644
index 00000000000..5e6b74d414b
--- /dev/null
+++ b/tests/ui/lint/decorate-ice/decorate-force-warn.stderr
@@ -0,0 +1,14 @@
+warning: unused return value of `f` that must be used
+  --> $DIR/decorate-force-warn.rs:11:5
+   |
+LL |     f();
+   |     ^^^
+   |
+   = note: requested on the command line with `--force-warn unused-must-use`
+help: use `let _ = ...` to ignore the resulting value
+   |
+LL |     let _ = f();
+   |     +++++++
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/lint/must_not_suspend/allocator.rs b/tests/ui/lint/must_not_suspend/allocator.rs
new file mode 100644
index 00000000000..c2ceb3297f3
--- /dev/null
+++ b/tests/ui/lint/must_not_suspend/allocator.rs
@@ -0,0 +1,30 @@
+//@ edition: 2021
+
+#![feature(must_not_suspend, allocator_api)]
+#![deny(must_not_suspend)]
+
+use std::alloc::*;
+use std::ptr::NonNull;
+
+#[must_not_suspend]
+struct MyAllocatorWhichMustNotSuspend;
+
+unsafe impl Allocator for MyAllocatorWhichMustNotSuspend {
+    fn allocate(&self, l: Layout) -> Result, AllocError> {
+        Global.allocate(l)
+    }
+    unsafe fn deallocate(&self, p: NonNull, l: Layout) {
+        Global.deallocate(p, l)
+    }
+}
+
+async fn suspend() {}
+
+async fn foo() {
+    let x = Box::new_in(1i32, MyAllocatorWhichMustNotSuspend);
+    //~^ ERROR allocator `MyAllocatorWhichMustNotSuspend` held across a suspend point, but should not be
+    suspend().await;
+    drop(x);
+}
+
+fn main() {}
diff --git a/tests/ui/lint/must_not_suspend/allocator.stderr b/tests/ui/lint/must_not_suspend/allocator.stderr
new file mode 100644
index 00000000000..959945f2626
--- /dev/null
+++ b/tests/ui/lint/must_not_suspend/allocator.stderr
@@ -0,0 +1,22 @@
+error: allocator `MyAllocatorWhichMustNotSuspend` held across a suspend point, but should not be
+  --> $DIR/allocator.rs:24:9
+   |
+LL |     let x = Box::new_in(1i32, MyAllocatorWhichMustNotSuspend);
+   |         ^
+LL |
+LL |     suspend().await;
+   |               ----- the value is held across this suspend point
+   |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+  --> $DIR/allocator.rs:24:9
+   |
+LL |     let x = Box::new_in(1i32, MyAllocatorWhichMustNotSuspend);
+   |         ^
+note: the lint level is defined here
+  --> $DIR/allocator.rs:4:9
+   |
+LL | #![deny(must_not_suspend)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/macros/paren-or-brace-expected.rs b/tests/ui/macros/paren-or-brace-expected.rs
new file mode 100644
index 00000000000..1776fa78884
--- /dev/null
+++ b/tests/ui/macros/paren-or-brace-expected.rs
@@ -0,0 +1,9 @@
+macro_rules! foo {
+    ( $( $i:ident ),* ) => {
+        $[count($i)]
+        //~^ ERROR expected `(` or `{`, found `[`
+        //~| ERROR
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/macros/paren-or-brace-expected.stderr b/tests/ui/macros/paren-or-brace-expected.stderr
new file mode 100644
index 00000000000..4d1dda97751
--- /dev/null
+++ b/tests/ui/macros/paren-or-brace-expected.stderr
@@ -0,0 +1,14 @@
+error: expected `(` or `{`, found `[`
+  --> $DIR/paren-or-brace-expected.rs:3:10
+   |
+LL |         $[count($i)]
+   |          ^^^^^^^^^^^
+
+error: expected one of: `*`, `+`, or `?`
+  --> $DIR/paren-or-brace-expected.rs:3:10
+   |
+LL |         $[count($i)]
+   |          ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs b/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs
new file mode 100644
index 00000000000..0235b291df5
--- /dev/null
+++ b/tests/ui/moves/nested-loop-moved-value-wrong-continue.rs
@@ -0,0 +1,50 @@
+fn foo() {
+    let foos = vec![String::new()];
+    let bars = vec![""];
+    let mut baz = vec![];
+    let mut qux = vec![];
+    for foo in foos { for bar in &bars { if foo == *bar {
+    //~^ NOTE this reinitialization might get skipped
+    //~| NOTE move occurs because `foo` has type `String`
+    //~| NOTE inside of this loop
+    //~| HELP consider moving the expression out of the loop
+    //~| NOTE in this expansion of desugaring of `for` loop
+        baz.push(foo);
+        //~^ NOTE value moved here
+        //~| HELP consider cloning the value
+        continue;
+        //~^ NOTE verify that your loop breaking logic is correct
+        //~| NOTE this `continue` advances the loop at $DIR/nested-loop-moved-value-wrong-continue.rs:6:23
+    } }
+    qux.push(foo);
+    //~^ ERROR use of moved value
+    //~| NOTE value used here
+    }
+}
+
+fn main() {
+    let foos = vec![String::new()];
+    let bars = vec![""];
+    let mut baz = vec![];
+    let mut qux = vec![];
+    for foo in foos {
+    //~^ NOTE this reinitialization might get skipped
+    //~| NOTE move occurs because `foo` has type `String`
+        for bar in &bars {
+        //~^ NOTE inside of this loop
+        //~| HELP consider moving the expression out of the loop
+        //~| NOTE in this expansion of desugaring of `for` loop
+            if foo == *bar {
+                baz.push(foo);
+                //~^ NOTE value moved here
+                //~| HELP consider cloning the value
+                continue;
+                //~^ NOTE verify that your loop breaking logic is correct
+                //~| NOTE this `continue` advances the loop at line 33
+            }
+        }
+        qux.push(foo);
+        //~^ ERROR use of moved value
+        //~| NOTE value used here
+    }
+}
diff --git a/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr b/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr
new file mode 100644
index 00000000000..3247513d42c
--- /dev/null
+++ b/tests/ui/moves/nested-loop-moved-value-wrong-continue.stderr
@@ -0,0 +1,83 @@
+error[E0382]: use of moved value: `foo`
+  --> $DIR/nested-loop-moved-value-wrong-continue.rs:19:14
+   |
+LL |     for foo in foos { for bar in &bars { if foo == *bar {
+   |         ---           ---------------- inside of this loop
+   |         |
+   |         this reinitialization might get skipped
+   |         move occurs because `foo` has type `String`, which does not implement the `Copy` trait
+...
+LL |         baz.push(foo);
+   |                  --- value moved here, in previous iteration of loop
+...
+LL |     qux.push(foo);
+   |              ^^^ value used here after move
+   |
+note: verify that your loop breaking logic is correct
+  --> $DIR/nested-loop-moved-value-wrong-continue.rs:15:9
+   |
+LL |     for foo in foos { for bar in &bars { if foo == *bar {
+   |     ---------------   ----------------
+...
+LL |         continue;
+   |         ^^^^^^^^ this `continue` advances the loop at $DIR/nested-loop-moved-value-wrong-continue.rs:6:23: 18:8
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~     for foo in foos { let mut value = baz.push(foo);
+LL ~     for bar in &bars { if foo == *bar {
+LL |
+ ...
+LL |
+LL ~         value;
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         baz.push(foo.clone());
+   |                     ++++++++
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/nested-loop-moved-value-wrong-continue.rs:46:18
+   |
+LL |     for foo in foos {
+   |         ---
+   |         |
+   |         this reinitialization might get skipped
+   |         move occurs because `foo` has type `String`, which does not implement the `Copy` trait
+...
+LL |         for bar in &bars {
+   |         ---------------- inside of this loop
+...
+LL |                 baz.push(foo);
+   |                          --- value moved here, in previous iteration of loop
+...
+LL |         qux.push(foo);
+   |                  ^^^ value used here after move
+   |
+note: verify that your loop breaking logic is correct
+  --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17
+   |
+LL |     for foo in foos {
+   |     ---------------
+...
+LL |         for bar in &bars {
+   |         ----------------
+...
+LL |                 continue;
+   |                 ^^^^^^^^ this `continue` advances the loop at line 33
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~         let mut value = baz.push(foo);
+LL ~         for bar in &bars {
+LL |
+ ...
+LL |             if foo == *bar {
+LL ~                 value;
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |                 baz.push(foo.clone());
+   |                             ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/moves/recreating-value-in-loop-condition.rs b/tests/ui/moves/recreating-value-in-loop-condition.rs
new file mode 100644
index 00000000000..ad012ff2978
--- /dev/null
+++ b/tests/ui/moves/recreating-value-in-loop-condition.rs
@@ -0,0 +1,65 @@
+fn iter(vec: Vec) -> impl Iterator {
+    vec.into_iter()
+}
+fn foo() {
+    let vec = vec!["one", "two", "three"];
+    while let Some(item) = iter(vec).next() { //~ ERROR use of moved value
+    //~^ HELP consider moving the expression out of the loop so it is only moved once
+        println!("{:?}", item);
+    }
+}
+fn bar() {
+    let vec = vec!["one", "two", "three"];
+    loop {
+    //~^ HELP consider moving the expression out of the loop so it is only moved once
+        let Some(item) = iter(vec).next() else { //~ ERROR use of moved value
+            break;
+        };
+        println!("{:?}", item);
+    }
+}
+fn baz() {
+    let vec = vec!["one", "two", "three"];
+    loop {
+    //~^ HELP consider moving the expression out of the loop so it is only moved once
+        let item = iter(vec).next(); //~ ERROR use of moved value
+        //~^ HELP consider cloning
+        if item.is_none() {
+            break;
+        }
+        println!("{:?}", item);
+    }
+}
+fn qux() {
+    let vec = vec!["one", "two", "three"];
+    loop {
+    //~^ HELP consider moving the expression out of the loop so it is only moved once
+        if let Some(item) = iter(vec).next() { //~ ERROR use of moved value
+            println!("{:?}", item);
+            break;
+        }
+    }
+}
+fn zap() {
+    loop {
+        let vec = vec!["one", "two", "three"];
+        loop {
+        //~^ HELP consider moving the expression out of the loop so it is only moved once
+            loop {
+                loop {
+                    if let Some(item) = iter(vec).next() { //~ ERROR use of moved value
+                        println!("{:?}", item);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
+fn main() {
+    foo();
+    bar();
+    baz();
+    qux();
+    zap();
+}
diff --git a/tests/ui/moves/recreating-value-in-loop-condition.stderr b/tests/ui/moves/recreating-value-in-loop-condition.stderr
new file mode 100644
index 00000000000..75c9633bc0a
--- /dev/null
+++ b/tests/ui/moves/recreating-value-in-loop-condition.stderr
@@ -0,0 +1,157 @@
+error[E0382]: use of moved value: `vec`
+  --> $DIR/recreating-value-in-loop-condition.rs:6:33
+   |
+LL |     let vec = vec!["one", "two", "three"];
+   |         --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
+LL |     while let Some(item) = iter(vec).next() {
+   |     ----------------------------^^^--------
+   |     |                           |
+   |     |                           value moved here, in previous iteration of loop
+   |     inside of this loop
+   |
+note: consider changing this parameter type in function `iter` to borrow instead if owning the value isn't necessary
+  --> $DIR/recreating-value-in-loop-condition.rs:1:17
+   |
+LL | fn iter(vec: Vec) -> impl Iterator {
+   |    ----         ^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~     let mut value = iter(vec);
+LL ~     while let Some(item) = value.next() {
+   |
+
+error[E0382]: use of moved value: `vec`
+  --> $DIR/recreating-value-in-loop-condition.rs:15:31
+   |
+LL |     let vec = vec!["one", "two", "three"];
+   |         --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
+LL |     loop {
+   |     ---- inside of this loop
+LL |
+LL |         let Some(item) = iter(vec).next() else {
+   |                               ^^^ value moved here, in previous iteration of loop
+   |
+note: consider changing this parameter type in function `iter` to borrow instead if owning the value isn't necessary
+  --> $DIR/recreating-value-in-loop-condition.rs:1:17
+   |
+LL | fn iter(vec: Vec) -> impl Iterator {
+   |    ----         ^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~     let mut value = iter(vec);
+LL ~     loop {
+LL |
+LL ~         let Some(item) = value.next() else {
+   |
+
+error[E0382]: use of moved value: `vec`
+  --> $DIR/recreating-value-in-loop-condition.rs:25:25
+   |
+LL |     let vec = vec!["one", "two", "three"];
+   |         --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
+LL |     loop {
+   |     ---- inside of this loop
+LL |
+LL |         let item = iter(vec).next();
+   |                         ^^^ value moved here, in previous iteration of loop
+   |
+note: consider changing this parameter type in function `iter` to borrow instead if owning the value isn't necessary
+  --> $DIR/recreating-value-in-loop-condition.rs:1:17
+   |
+LL | fn iter(vec: Vec) -> impl Iterator {
+   |    ----         ^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~     let mut value = iter(vec);
+LL ~     loop {
+LL |
+LL ~         let item = value.next();
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let item = iter(vec.clone()).next();
+   |                            ++++++++
+
+error[E0382]: use of moved value: `vec`
+  --> $DIR/recreating-value-in-loop-condition.rs:37:34
+   |
+LL |     let vec = vec!["one", "two", "three"];
+   |         --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
+LL |     loop {
+   |     ---- inside of this loop
+LL |
+LL |         if let Some(item) = iter(vec).next() {
+   |                                  ^^^ value moved here, in previous iteration of loop
+   |
+note: consider changing this parameter type in function `iter` to borrow instead if owning the value isn't necessary
+  --> $DIR/recreating-value-in-loop-condition.rs:1:17
+   |
+LL | fn iter(vec: Vec) -> impl Iterator {
+   |    ----         ^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~     let mut value = iter(vec);
+LL ~     loop {
+LL |
+LL ~         if let Some(item) = value.next() {
+   |
+
+error[E0382]: use of moved value: `vec`
+  --> $DIR/recreating-value-in-loop-condition.rs:50:46
+   |
+LL |         let vec = vec!["one", "two", "three"];
+   |             --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
+LL |         loop {
+   |         ---- inside of this loop
+LL |
+LL |             loop {
+   |             ---- inside of this loop
+LL |                 loop {
+   |                 ---- inside of this loop
+LL |                     if let Some(item) = iter(vec).next() {
+   |                                              ^^^ value moved here, in previous iteration of loop
+   |
+note: consider changing this parameter type in function `iter` to borrow instead if owning the value isn't necessary
+  --> $DIR/recreating-value-in-loop-condition.rs:1:17
+   |
+LL | fn iter(vec: Vec) -> impl Iterator {
+   |    ----         ^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+note: verify that your loop breaking logic is correct
+  --> $DIR/recreating-value-in-loop-condition.rs:52:25
+   |
+LL |     loop {
+   |     ----
+LL |         let vec = vec!["one", "two", "three"];
+LL |         loop {
+   |         ----
+LL |
+LL |             loop {
+   |             ----
+LL |                 loop {
+   |                 ----
+...
+LL |                         break;
+   |                         ^^^^^ this `break` exits the loop at line 49
+help: consider moving the expression out of the loop so it is only moved once
+   |
+LL ~         let mut value = iter(vec);
+LL ~         loop {
+LL |
+LL |             loop {
+LL |                 loop {
+LL ~                     if let Some(item) = value.next() {
+   |
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/never_type/issue-52443.stderr b/tests/ui/never_type/issue-52443.stderr
index 83afd9ef2f2..bab47064f6c 100644
--- a/tests/ui/never_type/issue-52443.stderr
+++ b/tests/ui/never_type/issue-52443.stderr
@@ -50,7 +50,10 @@ LL |     [(); { for _ in 0usize.. {}; 0}];
 note: impl defined here, but it is not `const`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0658]: mutable references are not allowed in constants
   --> $DIR/issue-52443.rs:9:21
@@ -69,7 +72,10 @@ LL |     [(); { for _ in 0usize.. {}; 0}];
    |                     ^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 6 previous errors; 1 warning emitted
 
diff --git a/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs b/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs
index 6566d8a1115..ed3ffed2f80 100644
--- a/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs
+++ b/tests/ui/parser/constraints-before-generic-args-syntactic-pass.rs
@@ -3,11 +3,7 @@
 #[cfg(FALSE)]
 fn syntax() {
     foo::();
-    //~^ WARN associated type bounds are unstable
-    //~| WARN unstable syntax
     foo::();
-    //~^ WARN associated type bounds are unstable
-    //~| WARN unstable syntax
 }
 
 fn main() {}
diff --git a/tests/ui/parser/constraints-before-generic-args-syntactic-pass.stderr b/tests/ui/parser/constraints-before-generic-args-syntactic-pass.stderr
deleted file mode 100644
index 393ed704b41..00000000000
--- a/tests/ui/parser/constraints-before-generic-args-syntactic-pass.stderr
+++ /dev/null
@@ -1,26 +0,0 @@
-warning: associated type bounds are unstable
-  --> $DIR/constraints-before-generic-args-syntactic-pass.rs:5:19
-   |
-LL |     foo::();
-   |                   ^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-   = warning: unstable syntax can change at any point in the future, causing a hard error!
-   = note: for more information, see issue #65860 
-
-warning: associated type bounds are unstable
-  --> $DIR/constraints-before-generic-args-syntactic-pass.rs:8:23
-   |
-LL |     foo::();
-   |                       ^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-   = warning: unstable syntax can change at any point in the future, causing a hard error!
-   = note: for more information, see issue #65860 
-
-warning: 2 warnings emitted
-
diff --git a/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr b/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr
index 29b0b25a564..9800420072b 100644
--- a/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr
+++ b/tests/ui/parser/impl-item-type-no-body-semantic-fail.stderr
@@ -89,12 +89,15 @@ LL |     type W: Ord where Self: Eq;
    |                       ^^^^^^^^ the trait `Eq` is not implemented for `X`
    |
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 help: consider annotating `X` with `#[derive(Eq)]`
    |
 LL + #[derive(Eq)]
 LL | struct X;
    |
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `X: Eq` is not satisfied
   --> $DIR/impl-item-type-no-body-semantic-fail.rs:18:18
@@ -103,12 +106,15 @@ LL |     type W where Self: Eq;
    |                  ^^^^^^^^ the trait `Eq` is not implemented for `X`
    |
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 help: consider annotating `X` with `#[derive(Eq)]`
    |
 LL + #[derive(Eq)]
 LL | struct X;
    |
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0592]: duplicate definitions with name `W`
   --> $DIR/impl-item-type-no-body-semantic-fail.rs:18:5
diff --git a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr
index 6a8cbd9389b..7b896ce1426 100644
--- a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr
+++ b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr
@@ -352,8 +352,11 @@ LL | static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]);
    |                                          ^^^^^^
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 40 previous errors
 
diff --git a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
index 98c66c9dd07..45bdba94d78 100644
--- a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
@@ -1,23 +1,23 @@
 error: unreachable pattern
-  --> $DIR/empty-types.rs:49:9
+  --> $DIR/empty-types.rs:51:9
    |
 LL |         _ => {}
    |         ^
    |
 note: the lint level is defined here
-  --> $DIR/empty-types.rs:15:9
+  --> $DIR/empty-types.rs:17:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:52:9
+  --> $DIR/empty-types.rs:54:9
    |
 LL |         _x => {}
    |         ^^
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
-  --> $DIR/empty-types.rs:56:11
+  --> $DIR/empty-types.rs:58:11
    |
 LL |     match ref_never {}
    |           ^^^^^^^^^
@@ -32,31 +32,31 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:71:9
+  --> $DIR/empty-types.rs:73:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:78:9
+  --> $DIR/empty-types.rs:80:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:81:9
+  --> $DIR/empty-types.rs:83:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:85:9
+  --> $DIR/empty-types.rs:87:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
-  --> $DIR/empty-types.rs:89:11
+  --> $DIR/empty-types.rs:91:11
    |
 LL |     match res_u32_never {}
    |           ^^^^^^^^^^^^^ pattern `Ok(_)` not covered
@@ -75,19 +75,19 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:97:9
+  --> $DIR/empty-types.rs:99:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:102:9
+  --> $DIR/empty-types.rs:104:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
-  --> $DIR/empty-types.rs:99:11
+  --> $DIR/empty-types.rs:101:11
    |
 LL |     match res_u32_never {
    |           ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
@@ -105,7 +105,7 @@ LL ~         Ok(1_u32..=u32::MAX) => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-types.rs:106:9
+  --> $DIR/empty-types.rs:108:9
    |
 LL |     let Ok(_x) = res_u32_never.as_ref();
    |         ^^^^^^ pattern `Err(_)` not covered
@@ -119,121 +119,121 @@ LL |     let Ok(_x) = res_u32_never.as_ref() else { todo!() };
    |                                         ++++++++++++++++
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:117:9
+  --> $DIR/empty-types.rs:119:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:121:9
+  --> $DIR/empty-types.rs:123:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:124:9
+  --> $DIR/empty-types.rs:126:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:125:9
+  --> $DIR/empty-types.rs:127:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:128:9
+  --> $DIR/empty-types.rs:130:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:129:9
+  --> $DIR/empty-types.rs:131:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:138:13
+  --> $DIR/empty-types.rs:140:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:141:13
+  --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:150:13
+  --> $DIR/empty-types.rs:152:13
    |
 LL |             Some(_) => {}
    |             ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:154:13
+  --> $DIR/empty-types.rs:156:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:206:13
+  --> $DIR/empty-types.rs:208:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:211:13
+  --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:216:13
+  --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:221:13
+  --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:227:13
+  --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:286:9
+  --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:289:9
+  --> $DIR/empty-types.rs:291:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:292:9
+  --> $DIR/empty-types.rs:294:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:293:9
+  --> $DIR/empty-types.rs:295:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
-  --> $DIR/empty-types.rs:325:11
+  --> $DIR/empty-types.rs:327:11
    |
 LL |     match slice_never {}
    |           ^^^^^^^^^^^
@@ -247,7 +247,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: `&[]` not covered
-  --> $DIR/empty-types.rs:336:11
+  --> $DIR/empty-types.rs:338:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ pattern `&[]` not covered
@@ -260,7 +260,7 @@ LL +         &[] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `&[]` not covered
-  --> $DIR/empty-types.rs:349:11
+  --> $DIR/empty-types.rs:352:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ pattern `&[]` not covered
@@ -274,7 +274,7 @@ LL +         &[] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
-  --> $DIR/empty-types.rs:355:11
+  --> $DIR/empty-types.rs:359:11
    |
 LL |     match *slice_never {}
    |           ^^^^^^^^^^^^
@@ -288,25 +288,25 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:365:9
+  --> $DIR/empty-types.rs:369:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:368:9
+  --> $DIR/empty-types.rs:372:9
    |
 LL |         [_, _, _] => {}
    |         ^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:371:9
+  --> $DIR/empty-types.rs:375:9
    |
 LL |         [_, ..] => {}
    |         ^^^^^^^
 
 error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
-  --> $DIR/empty-types.rs:385:11
+  --> $DIR/empty-types.rs:389:11
    |
 LL |     match array_0_never {}
    |           ^^^^^^^^^^^^^
@@ -320,13 +320,13 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:392:9
+  --> $DIR/empty-types.rs:396:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
-  --> $DIR/empty-types.rs:394:11
+  --> $DIR/empty-types.rs:398:11
    |
 LL |     match array_0_never {
    |           ^^^^^^^^^^^^^ pattern `[]` not covered
@@ -340,49 +340,49 @@ LL +         [] => todo!()
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:413:9
+  --> $DIR/empty-types.rs:417:9
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:418:9
+  --> $DIR/empty-types.rs:422:9
    |
 LL |         Some(_a) => {}
    |         ^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:423:9
+  --> $DIR/empty-types.rs:427:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:428:9
+  --> $DIR/empty-types.rs:432:9
    |
 LL |         _a => {}
    |         ^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:600:9
+  --> $DIR/empty-types.rs:604:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:603:9
+  --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:606:9
+  --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:609:9
+  --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
diff --git a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
index d5121e7043c..6e50dfe6a26 100644
--- a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
@@ -1,23 +1,23 @@
 error: unreachable pattern
-  --> $DIR/empty-types.rs:49:9
+  --> $DIR/empty-types.rs:51:9
    |
 LL |         _ => {}
    |         ^
    |
 note: the lint level is defined here
-  --> $DIR/empty-types.rs:15:9
+  --> $DIR/empty-types.rs:17:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:52:9
+  --> $DIR/empty-types.rs:54:9
    |
 LL |         _x => {}
    |         ^^
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
-  --> $DIR/empty-types.rs:56:11
+  --> $DIR/empty-types.rs:58:11
    |
 LL |     match ref_never {}
    |           ^^^^^^^^^
@@ -32,31 +32,31 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:71:9
+  --> $DIR/empty-types.rs:73:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:78:9
+  --> $DIR/empty-types.rs:80:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:81:9
+  --> $DIR/empty-types.rs:83:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:85:9
+  --> $DIR/empty-types.rs:87:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
-  --> $DIR/empty-types.rs:89:11
+  --> $DIR/empty-types.rs:91:11
    |
 LL |     match res_u32_never {}
    |           ^^^^^^^^^^^^^ pattern `Ok(_)` not covered
@@ -75,19 +75,19 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:97:9
+  --> $DIR/empty-types.rs:99:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:102:9
+  --> $DIR/empty-types.rs:104:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
-  --> $DIR/empty-types.rs:99:11
+  --> $DIR/empty-types.rs:101:11
    |
 LL |     match res_u32_never {
    |           ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
@@ -105,7 +105,7 @@ LL ~         Ok(1_u32..=u32::MAX) => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-types.rs:106:9
+  --> $DIR/empty-types.rs:108:9
    |
 LL |     let Ok(_x) = res_u32_never.as_ref();
    |         ^^^^^^ pattern `Err(_)` not covered
@@ -119,7 +119,7 @@ LL |     let Ok(_x) = res_u32_never.as_ref() else { todo!() };
    |                                         ++++++++++++++++
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-types.rs:110:9
+  --> $DIR/empty-types.rs:112:9
    |
 LL |     let Ok(_x) = &res_u32_never;
    |         ^^^^^^ pattern `&Err(_)` not covered
@@ -133,67 +133,67 @@ LL |     let Ok(_x) = &res_u32_never else { todo!() };
    |                                 ++++++++++++++++
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:117:9
+  --> $DIR/empty-types.rs:119:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:121:9
+  --> $DIR/empty-types.rs:123:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:124:9
+  --> $DIR/empty-types.rs:126:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:125:9
+  --> $DIR/empty-types.rs:127:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:128:9
+  --> $DIR/empty-types.rs:130:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:129:9
+  --> $DIR/empty-types.rs:131:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:138:13
+  --> $DIR/empty-types.rs:140:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:141:13
+  --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:150:13
+  --> $DIR/empty-types.rs:152:13
    |
 LL |             Some(_) => {}
    |             ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:154:13
+  --> $DIR/empty-types.rs:156:13
    |
 LL |             _ => {}
    |             ^
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:163:15
+  --> $DIR/empty-types.rs:165:15
    |
 LL |         match *ref_opt_void {
    |               ^^^^^^^^^^^^^ pattern `Some(_)` not covered
@@ -211,61 +211,61 @@ LL +             Some(_) => todo!()
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:206:13
+  --> $DIR/empty-types.rs:208:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:211:13
+  --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:216:13
+  --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:221:13
+  --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:227:13
+  --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:286:9
+  --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:289:9
+  --> $DIR/empty-types.rs:291:9
    |
 LL |         (_, _) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:292:9
+  --> $DIR/empty-types.rs:294:9
    |
 LL |         Ok(_) => {}
    |         ^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:293:9
+  --> $DIR/empty-types.rs:295:9
    |
 LL |         Err(_) => {}
    |         ^^^^^^
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
-  --> $DIR/empty-types.rs:314:11
+  --> $DIR/empty-types.rs:316:11
    |
 LL |     match *x {}
    |           ^^
@@ -279,7 +279,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
-  --> $DIR/empty-types.rs:316:11
+  --> $DIR/empty-types.rs:318:11
    |
 LL |     match *x {}
    |           ^^
@@ -293,7 +293,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
-  --> $DIR/empty-types.rs:318:11
+  --> $DIR/empty-types.rs:320:11
    |
 LL |     match *x {}
    |           ^^ patterns `Ok(_)` and `Err(_)` not covered
@@ -315,7 +315,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
-  --> $DIR/empty-types.rs:320:11
+  --> $DIR/empty-types.rs:322:11
    |
 LL |     match *x {}
    |           ^^
@@ -329,7 +329,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
-  --> $DIR/empty-types.rs:325:11
+  --> $DIR/empty-types.rs:327:11
    |
 LL |     match slice_never {}
    |           ^^^^^^^^^^^
@@ -343,7 +343,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
-  --> $DIR/empty-types.rs:327:11
+  --> $DIR/empty-types.rs:329:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ pattern `&[_, ..]` not covered
@@ -356,7 +356,7 @@ LL +         &[_, ..] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered
-  --> $DIR/empty-types.rs:336:11
+  --> $DIR/empty-types.rs:338:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered
@@ -369,7 +369,7 @@ LL +         &[] | &[_] | &[_, _] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered
-  --> $DIR/empty-types.rs:349:11
+  --> $DIR/empty-types.rs:352:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered
@@ -383,7 +383,7 @@ LL +         &[] | &[_, ..] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
-  --> $DIR/empty-types.rs:355:11
+  --> $DIR/empty-types.rs:359:11
    |
 LL |     match *slice_never {}
    |           ^^^^^^^^^^^^
@@ -397,25 +397,25 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:365:9
+  --> $DIR/empty-types.rs:369:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:368:9
+  --> $DIR/empty-types.rs:372:9
    |
 LL |         [_, _, _] => {}
    |         ^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:371:9
+  --> $DIR/empty-types.rs:375:9
    |
 LL |         [_, ..] => {}
    |         ^^^^^^^
 
 error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
-  --> $DIR/empty-types.rs:385:11
+  --> $DIR/empty-types.rs:389:11
    |
 LL |     match array_0_never {}
    |           ^^^^^^^^^^^^^
@@ -429,13 +429,13 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:392:9
+  --> $DIR/empty-types.rs:396:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
-  --> $DIR/empty-types.rs:394:11
+  --> $DIR/empty-types.rs:398:11
    |
 LL |     match array_0_never {
    |           ^^^^^^^^^^^^^ pattern `[]` not covered
@@ -449,31 +449,31 @@ LL +         [] => todo!()
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:413:9
+  --> $DIR/empty-types.rs:417:9
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:418:9
+  --> $DIR/empty-types.rs:422:9
    |
 LL |         Some(_a) => {}
    |         ^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:423:9
+  --> $DIR/empty-types.rs:427:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:428:9
+  --> $DIR/empty-types.rs:432:9
    |
 LL |         _a => {}
    |         ^^
 
 error[E0004]: non-exhaustive patterns: `&Some(_)` not covered
-  --> $DIR/empty-types.rs:448:11
+  --> $DIR/empty-types.rs:452:11
    |
 LL |     match ref_opt_never {
    |           ^^^^^^^^^^^^^ pattern `&Some(_)` not covered
@@ -491,7 +491,7 @@ LL +         &Some(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:489:11
+  --> $DIR/empty-types.rs:493:11
    |
 LL |     match *ref_opt_never {
    |           ^^^^^^^^^^^^^^ pattern `Some(_)` not covered
@@ -509,7 +509,7 @@ LL +         Some(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/empty-types.rs:537:11
+  --> $DIR/empty-types.rs:541:11
    |
 LL |     match *ref_res_never {
    |           ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
@@ -527,7 +527,7 @@ LL +         Err(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/empty-types.rs:548:11
+  --> $DIR/empty-types.rs:552:11
    |
 LL |     match *ref_res_never {
    |           ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
@@ -545,7 +545,7 @@ LL +         Err(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
-  --> $DIR/empty-types.rs:567:11
+  --> $DIR/empty-types.rs:571:11
    |
 LL |     match *ref_tuple_half_never {}
    |           ^^^^^^^^^^^^^^^^^^^^^
@@ -559,31 +559,31 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:600:9
+  --> $DIR/empty-types.rs:604:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:603:9
+  --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:606:9
+  --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:609:9
+  --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
 
 error[E0004]: non-exhaustive patterns: `&_` not covered
-  --> $DIR/empty-types.rs:634:11
+  --> $DIR/empty-types.rs:638:11
    |
 LL |     match ref_never {
    |           ^^^^^^^^^ pattern `&_` not covered
@@ -597,8 +597,26 @@ LL ~         &_a if false => {},
 LL +         &_ => todo!()
    |
 
+error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
+  --> $DIR/empty-types.rs:654:11
+   |
+LL |     match *ref_result_never {
+   |           ^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Err(_) => {},
+LL +         Ok(_) => todo!()
+   |
+
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:662:11
+  --> $DIR/empty-types.rs:674:11
    |
 LL |     match *x {
    |           ^^ pattern `Some(_)` not covered
@@ -615,7 +633,7 @@ LL ~         None => {},
 LL +         Some(_) => todo!()
    |
 
-error: aborting due to 63 previous errors
+error: aborting due to 64 previous errors
 
 Some errors have detailed explanations: E0004, E0005.
 For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr
new file mode 100644
index 00000000000..0ff2472922e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr
@@ -0,0 +1,644 @@
+warning: the feature `never_patterns` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/empty-types.rs:14:33
+   |
+LL | #![cfg_attr(never_pats, feature(never_patterns))]
+   |                                 ^^^^^^^^^^^^^^
+   |
+   = note: see issue #118155  for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:51:9
+   |
+LL |         _ => {}
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-types.rs:17:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:54:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error[E0004]: non-exhaustive patterns: type `&!` is non-empty
+  --> $DIR/empty-types.rs:58:11
+   |
+LL |     match ref_never {}
+   |           ^^^^^^^^^
+   |
+   = note: the matched value is of type `&!`
+   = note: references are always considered inhabited
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match ref_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
+  --> $DIR/empty-types.rs:70:11
+   |
+LL |     match tuple_half_never {}
+   |           ^^^^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `(u32, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match tuple_half_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
+  --> $DIR/empty-types.rs:77:11
+   |
+LL |     match tuple_never {}
+   |           ^^^^^^^^^^^
+   |
+   = note: the matched value is of type `(!, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match tuple_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:87:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(!)` not covered
+  --> $DIR/empty-types.rs:91:11
+   |
+LL |     match res_u32_never {}
+   |           ^^^^^^^^^^^^^ patterns `Ok(_)` and `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~     match res_u32_never {
+LL +         Ok(_) | Err(!) => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(!)` not covered
+  --> $DIR/empty-types.rs:93:11
+   |
+LL |     match res_u32_never {
+   |           ^^^^^^^^^^^^^ pattern `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Ok(_) => {},
+LL +         Err(!)
+   |
+
+error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
+  --> $DIR/empty-types.rs:101:11
+   |
+LL |     match res_u32_never {
+   |           ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Err(_) => {},
+LL ~         Ok(1_u32..=u32::MAX) => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:106:9
+   |
+LL |     let Ok(_x) = res_u32_never;
+   |         ^^^^^^ pattern `Err(!)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Result`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = res_u32_never else { todo!() };
+   |                                ++++++++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:108:9
+   |
+LL |     let Ok(_x) = res_u32_never.as_ref();
+   |         ^^^^^^ pattern `Err(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Result<&u32, &!>`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = res_u32_never.as_ref() else { todo!() };
+   |                                         ++++++++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/empty-types.rs:112:9
+   |
+LL |     let Ok(_x) = &res_u32_never;
+   |         ^^^^^^ pattern `&Err(!)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `&Result`
+help: you might want to use `let else` to handle the variant that isn't matched
+   |
+LL |     let Ok(_x) = &res_u32_never else { todo!() };
+   |                                 ++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `Ok(!)` and `Err(!)` not covered
+  --> $DIR/empty-types.rs:116:11
+   |
+LL |     match result_never {}
+   |           ^^^^^^^^^^^^ patterns `Ok(!)` and `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~     match result_never {
+LL +         Ok(!) | Err(!),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(!)` not covered
+  --> $DIR/empty-types.rs:121:11
+   |
+LL |     match result_never {
+   |           ^^^^^^^^^^^^ pattern `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |         Ok(_) => {}, Err(!)
+   |                    ++++++++
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:140:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:143:13
+   |
+LL |             _ if false => {}
+   |             ^
+
+error[E0004]: non-exhaustive patterns: `Some(!)` not covered
+  --> $DIR/empty-types.rs:146:15
+   |
+LL |         match opt_void {
+   |               ^^^^^^^^ pattern `Some(!)` not covered
+   |
+note: `Option` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~             None => {},
+LL +             Some(!)
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(!)` not covered
+  --> $DIR/empty-types.rs:165:15
+   |
+LL |         match *ref_opt_void {
+   |               ^^^^^^^^^^^^^ pattern `Some(!)` not covered
+   |
+note: `Option` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~             None => {},
+LL +             Some(!)
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:208:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:213:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:218:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:223:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:229:13
+   |
+LL |             _ => {}
+   |             ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:288:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
+  --> $DIR/empty-types.rs:316:11
+   |
+LL |     match *x {}
+   |           ^^
+   |
+   = note: the matched value is of type `(u32, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
+  --> $DIR/empty-types.rs:318:11
+   |
+LL |     match *x {}
+   |           ^^
+   |
+   = note: the matched value is of type `(!, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: `Ok(!)` and `Err(!)` not covered
+  --> $DIR/empty-types.rs:320:11
+   |
+LL |     match *x {}
+   |           ^^ patterns `Ok(!)` and `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~     match *x {
+LL +         Ok(!) | Err(!),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
+  --> $DIR/empty-types.rs:322:11
+   |
+LL |     match *x {}
+   |           ^^
+   |
+   = note: the matched value is of type `[!; 3]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
+  --> $DIR/empty-types.rs:327:11
+   |
+LL |     match slice_never {}
+   |           ^^^^^^^^^^^
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match slice_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: `&[!, ..]` not covered
+  --> $DIR/empty-types.rs:329:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ pattern `&[!, ..]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         [] => {},
+LL +         &[!, ..]
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[!]` and `&[!, !]` not covered
+  --> $DIR/empty-types.rs:338:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ patterns `&[]`, `&[!]` and `&[!, !]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~         [_, _, _, ..] => {},
+LL +         &[] | &[!] | &[!, !] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[!, ..]` not covered
+  --> $DIR/empty-types.rs:352:11
+   |
+LL |     match slice_never {
+   |           ^^^^^^^^^^^ patterns `&[]` and `&[!, ..]` not covered
+   |
+   = note: the matched value is of type `&[!]`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~         &[..] if false => {},
+LL +         &[] | &[!, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
+  --> $DIR/empty-types.rs:359:11
+   |
+LL |     match *slice_never {}
+   |           ^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *slice_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
+  --> $DIR/empty-types.rs:366:11
+   |
+LL |     match array_3_never {}
+   |           ^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!; 3]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match array_3_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
+  --> $DIR/empty-types.rs:389:11
+   |
+LL |     match array_0_never {}
+   |           ^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `[!; 0]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match array_0_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:396:9
+   |
+LL |         _ => {}
+   |         ^
+
+error[E0004]: non-exhaustive patterns: `[]` not covered
+  --> $DIR/empty-types.rs:398:11
+   |
+LL |     match array_0_never {
+   |           ^^^^^^^^^^^^^ pattern `[]` not covered
+   |
+   = note: the matched value is of type `[!; 0]`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         [..] if false => {},
+LL +         [] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&Some(!)` not covered
+  --> $DIR/empty-types.rs:452:11
+   |
+LL |     match ref_opt_never {
+   |           ^^^^^^^^^^^^^ pattern `&Some(!)` not covered
+   |
+note: `Option` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `&Option`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         &None => {},
+LL +         &Some(!)
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(!)` not covered
+  --> $DIR/empty-types.rs:493:11
+   |
+LL |     match *ref_opt_never {
+   |           ^^^^^^^^^^^^^^ pattern `Some(!)` not covered
+   |
+note: `Option` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL +         Some(!)
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(!)` not covered
+  --> $DIR/empty-types.rs:541:11
+   |
+LL |     match *ref_res_never {
+   |           ^^^^^^^^^^^^^^ pattern `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Ok(_) => {},
+LL +         Err(!)
+   |
+
+error[E0004]: non-exhaustive patterns: `Err(!)` not covered
+  --> $DIR/empty-types.rs:552:11
+   |
+LL |     match *ref_res_never {
+   |           ^^^^^^^^^^^^^^ pattern `Err(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Ok(_a) => {},
+LL +         Err(!)
+   |
+
+error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
+  --> $DIR/empty-types.rs:571:11
+   |
+LL |     match *ref_tuple_half_never {}
+   |           ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `(u32, !)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match *ref_tuple_half_never {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:604:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:607:9
+   |
+LL |         _x => {}
+   |         ^^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:610:9
+   |
+LL |         _ if false => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-types.rs:613:9
+   |
+LL |         _x if false => {}
+   |         ^^
+
+error[E0004]: non-exhaustive patterns: `&!` not covered
+  --> $DIR/empty-types.rs:638:11
+   |
+LL |     match ref_never {
+   |           ^^^^^^^^^ pattern `&!` not covered
+   |
+   = note: the matched value is of type `&!`
+   = note: references are always considered inhabited
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         &_a if false => {},
+LL +         &!
+   |
+
+error[E0004]: non-exhaustive patterns: `Ok(!)` not covered
+  --> $DIR/empty-types.rs:654:11
+   |
+LL |     match *ref_result_never {
+   |           ^^^^^^^^^^^^^^^^^ pattern `Ok(!)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Err(_) => {},
+LL +         Ok(!)
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(!)` not covered
+  --> $DIR/empty-types.rs:674:11
+   |
+LL |     match *x {
+   |           ^^ pattern `Some(!)` not covered
+   |
+note: `Option>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL +         Some(!)
+   |
+
+error: aborting due to 49 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0004, E0005.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr
index dc01ac4ddce..1d13802a2bd 100644
--- a/tests/ui/pattern/usefulness/empty-types.normal.stderr
+++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr
@@ -1,23 +1,23 @@
 error: unreachable pattern
-  --> $DIR/empty-types.rs:49:9
+  --> $DIR/empty-types.rs:51:9
    |
 LL |         _ => {}
    |         ^
    |
 note: the lint level is defined here
-  --> $DIR/empty-types.rs:15:9
+  --> $DIR/empty-types.rs:17:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:52:9
+  --> $DIR/empty-types.rs:54:9
    |
 LL |         _x => {}
    |         ^^
 
 error[E0004]: non-exhaustive patterns: type `&!` is non-empty
-  --> $DIR/empty-types.rs:56:11
+  --> $DIR/empty-types.rs:58:11
    |
 LL |     match ref_never {}
    |           ^^^^^^^^^
@@ -32,7 +32,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
-  --> $DIR/empty-types.rs:68:11
+  --> $DIR/empty-types.rs:70:11
    |
 LL |     match tuple_half_never {}
    |           ^^^^^^^^^^^^^^^^
@@ -46,7 +46,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
-  --> $DIR/empty-types.rs:75:11
+  --> $DIR/empty-types.rs:77:11
    |
 LL |     match tuple_never {}
    |           ^^^^^^^^^^^
@@ -60,13 +60,13 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:85:9
+  --> $DIR/empty-types.rs:87:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
-  --> $DIR/empty-types.rs:89:11
+  --> $DIR/empty-types.rs:91:11
    |
 LL |     match res_u32_never {}
    |           ^^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered
@@ -88,7 +88,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/empty-types.rs:91:11
+  --> $DIR/empty-types.rs:93:11
    |
 LL |     match res_u32_never {
    |           ^^^^^^^^^^^^^ pattern `Err(_)` not covered
@@ -106,7 +106,7 @@ LL +         Err(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
-  --> $DIR/empty-types.rs:99:11
+  --> $DIR/empty-types.rs:101:11
    |
 LL |     match res_u32_never {
    |           ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
@@ -124,7 +124,7 @@ LL ~         Ok(1_u32..=u32::MAX) => todo!()
    |
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-types.rs:104:9
+  --> $DIR/empty-types.rs:106:9
    |
 LL |     let Ok(_x) = res_u32_never;
    |         ^^^^^^ pattern `Err(_)` not covered
@@ -138,7 +138,7 @@ LL |     let Ok(_x) = res_u32_never else { todo!() };
    |                                ++++++++++++++++
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-types.rs:106:9
+  --> $DIR/empty-types.rs:108:9
    |
 LL |     let Ok(_x) = res_u32_never.as_ref();
    |         ^^^^^^ pattern `Err(_)` not covered
@@ -152,7 +152,7 @@ LL |     let Ok(_x) = res_u32_never.as_ref() else { todo!() };
    |                                         ++++++++++++++++
 
 error[E0005]: refutable pattern in local binding
-  --> $DIR/empty-types.rs:110:9
+  --> $DIR/empty-types.rs:112:9
    |
 LL |     let Ok(_x) = &res_u32_never;
    |         ^^^^^^ pattern `&Err(_)` not covered
@@ -166,7 +166,7 @@ LL |     let Ok(_x) = &res_u32_never else { todo!() };
    |                                 ++++++++++++++++
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
-  --> $DIR/empty-types.rs:114:11
+  --> $DIR/empty-types.rs:116:11
    |
 LL |     match result_never {}
    |           ^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered
@@ -188,7 +188,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/empty-types.rs:119:11
+  --> $DIR/empty-types.rs:121:11
    |
 LL |     match result_never {
    |           ^^^^^^^^^^^^ pattern `Err(_)` not covered
@@ -205,19 +205,19 @@ LL |         Ok(_) => {}, Err(_) => todo!()
    |                    +++++++++++++++++++
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:138:13
+  --> $DIR/empty-types.rs:140:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:141:13
+  --> $DIR/empty-types.rs:143:13
    |
 LL |             _ if false => {}
    |             ^
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:144:15
+  --> $DIR/empty-types.rs:146:15
    |
 LL |         match opt_void {
    |               ^^^^^^^^ pattern `Some(_)` not covered
@@ -235,7 +235,7 @@ LL +             Some(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:163:15
+  --> $DIR/empty-types.rs:165:15
    |
 LL |         match *ref_opt_void {
    |               ^^^^^^^^^^^^^ pattern `Some(_)` not covered
@@ -253,43 +253,43 @@ LL +             Some(_) => todo!()
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:206:13
+  --> $DIR/empty-types.rs:208:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:211:13
+  --> $DIR/empty-types.rs:213:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:216:13
+  --> $DIR/empty-types.rs:218:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:221:13
+  --> $DIR/empty-types.rs:223:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:227:13
+  --> $DIR/empty-types.rs:229:13
    |
 LL |             _ => {}
    |             ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:286:9
+  --> $DIR/empty-types.rs:288:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
-  --> $DIR/empty-types.rs:314:11
+  --> $DIR/empty-types.rs:316:11
    |
 LL |     match *x {}
    |           ^^
@@ -303,7 +303,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
-  --> $DIR/empty-types.rs:316:11
+  --> $DIR/empty-types.rs:318:11
    |
 LL |     match *x {}
    |           ^^
@@ -317,7 +317,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
-  --> $DIR/empty-types.rs:318:11
+  --> $DIR/empty-types.rs:320:11
    |
 LL |     match *x {}
    |           ^^ patterns `Ok(_)` and `Err(_)` not covered
@@ -339,7 +339,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
-  --> $DIR/empty-types.rs:320:11
+  --> $DIR/empty-types.rs:322:11
    |
 LL |     match *x {}
    |           ^^
@@ -353,7 +353,7 @@ LL ~     }
    |
 
 error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
-  --> $DIR/empty-types.rs:325:11
+  --> $DIR/empty-types.rs:327:11
    |
 LL |     match slice_never {}
    |           ^^^^^^^^^^^
@@ -367,7 +367,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
-  --> $DIR/empty-types.rs:327:11
+  --> $DIR/empty-types.rs:329:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ pattern `&[_, ..]` not covered
@@ -380,7 +380,7 @@ LL +         &[_, ..] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered
-  --> $DIR/empty-types.rs:336:11
+  --> $DIR/empty-types.rs:338:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered
@@ -393,7 +393,7 @@ LL +         &[] | &[_] | &[_, _] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered
-  --> $DIR/empty-types.rs:349:11
+  --> $DIR/empty-types.rs:352:11
    |
 LL |     match slice_never {
    |           ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered
@@ -407,7 +407,7 @@ LL +         &[] | &[_, ..] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
-  --> $DIR/empty-types.rs:355:11
+  --> $DIR/empty-types.rs:359:11
    |
 LL |     match *slice_never {}
    |           ^^^^^^^^^^^^
@@ -421,7 +421,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
-  --> $DIR/empty-types.rs:362:11
+  --> $DIR/empty-types.rs:366:11
    |
 LL |     match array_3_never {}
    |           ^^^^^^^^^^^^^
@@ -435,7 +435,7 @@ LL +     }
    |
 
 error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
-  --> $DIR/empty-types.rs:385:11
+  --> $DIR/empty-types.rs:389:11
    |
 LL |     match array_0_never {}
    |           ^^^^^^^^^^^^^
@@ -449,13 +449,13 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:392:9
+  --> $DIR/empty-types.rs:396:9
    |
 LL |         _ => {}
    |         ^
 
 error[E0004]: non-exhaustive patterns: `[]` not covered
-  --> $DIR/empty-types.rs:394:11
+  --> $DIR/empty-types.rs:398:11
    |
 LL |     match array_0_never {
    |           ^^^^^^^^^^^^^ pattern `[]` not covered
@@ -469,7 +469,7 @@ LL +         [] => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `&Some(_)` not covered
-  --> $DIR/empty-types.rs:448:11
+  --> $DIR/empty-types.rs:452:11
    |
 LL |     match ref_opt_never {
    |           ^^^^^^^^^^^^^ pattern `&Some(_)` not covered
@@ -487,7 +487,7 @@ LL +         &Some(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:489:11
+  --> $DIR/empty-types.rs:493:11
    |
 LL |     match *ref_opt_never {
    |           ^^^^^^^^^^^^^^ pattern `Some(_)` not covered
@@ -505,7 +505,7 @@ LL +         Some(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/empty-types.rs:537:11
+  --> $DIR/empty-types.rs:541:11
    |
 LL |     match *ref_res_never {
    |           ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
@@ -523,7 +523,7 @@ LL +         Err(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/empty-types.rs:548:11
+  --> $DIR/empty-types.rs:552:11
    |
 LL |     match *ref_res_never {
    |           ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
@@ -541,7 +541,7 @@ LL +         Err(_) => todo!()
    |
 
 error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
-  --> $DIR/empty-types.rs:567:11
+  --> $DIR/empty-types.rs:571:11
    |
 LL |     match *ref_tuple_half_never {}
    |           ^^^^^^^^^^^^^^^^^^^^^
@@ -555,31 +555,31 @@ LL +     }
    |
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:600:9
+  --> $DIR/empty-types.rs:604:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:603:9
+  --> $DIR/empty-types.rs:607:9
    |
 LL |         _x => {}
    |         ^^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:606:9
+  --> $DIR/empty-types.rs:610:9
    |
 LL |         _ if false => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/empty-types.rs:609:9
+  --> $DIR/empty-types.rs:613:9
    |
 LL |         _x if false => {}
    |         ^^
 
 error[E0004]: non-exhaustive patterns: `&_` not covered
-  --> $DIR/empty-types.rs:634:11
+  --> $DIR/empty-types.rs:638:11
    |
 LL |     match ref_never {
    |           ^^^^^^^^^ pattern `&_` not covered
@@ -593,8 +593,26 @@ LL ~         &_a if false => {},
 LL +         &_ => todo!()
    |
 
+error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
+  --> $DIR/empty-types.rs:654:11
+   |
+LL |     match *ref_result_never {
+   |           ^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered
+   |
+note: `Result` defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+  ::: $SRC_DIR/core/src/result.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Result`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         Err(_) => {},
+LL +         Ok(_) => todo!()
+   |
+
 error[E0004]: non-exhaustive patterns: `Some(_)` not covered
-  --> $DIR/empty-types.rs:662:11
+  --> $DIR/empty-types.rs:674:11
    |
 LL |     match *x {
    |           ^^ pattern `Some(_)` not covered
@@ -611,7 +629,7 @@ LL ~         None => {},
 LL +         Some(_) => todo!()
    |
 
-error: aborting due to 48 previous errors
+error: aborting due to 49 previous errors
 
 Some errors have detailed explanations: E0004, E0005.
 For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs
index 06651613010..cc71f67831d 100644
--- a/tests/ui/pattern/usefulness/empty-types.rs
+++ b/tests/ui/pattern/usefulness/empty-types.rs
@@ -1,4 +1,4 @@
-//@ revisions: normal min_exh_pats exhaustive_patterns
+//@ revisions: normal min_exh_pats exhaustive_patterns never_pats
 // gate-test-min_exhaustive_patterns
 //
 // This tests correct handling of empty types in exhaustiveness checking.
@@ -11,6 +11,8 @@
 #![feature(never_type_fallback)]
 #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
 #![cfg_attr(min_exh_pats, feature(min_exhaustive_patterns))]
+#![cfg_attr(never_pats, feature(never_patterns))]
+//[never_pats]~^ WARN the feature `never_patterns` is incomplete
 #![allow(dead_code, unreachable_code)]
 #![deny(unreachable_patterns)]
 
@@ -66,14 +68,14 @@ fn basic(x: NeverBundle) {
 
     let tuple_half_never: (u32, !) = x.tuple_half_never;
     match tuple_half_never {}
-    //[normal]~^ ERROR non-empty
+    //[normal,never_pats]~^ ERROR non-empty
     match tuple_half_never {
         (_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
     }
 
     let tuple_never: (!, !) = x.tuple_never;
     match tuple_never {}
-    //[normal]~^ ERROR non-empty
+    //[normal,never_pats]~^ ERROR non-empty
     match tuple_never {
         _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
     }
@@ -89,7 +91,7 @@ fn basic(x: NeverBundle) {
     match res_u32_never {}
     //~^ ERROR non-exhaustive
     match res_u32_never {
-        //[normal]~^ ERROR non-exhaustive
+        //[normal,never_pats]~^ ERROR non-exhaustive
         Ok(_) => {}
     }
     match res_u32_never {
@@ -102,22 +104,22 @@ fn basic(x: NeverBundle) {
         Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
     }
     let Ok(_x) = res_u32_never;
-    //[normal]~^ ERROR refutable
+    //[normal,never_pats]~^ ERROR refutable
     let Ok(_x) = res_u32_never.as_ref();
     //~^ ERROR refutable
     // Non-obvious difference: here there's an implicit dereference in the patterns, which makes the
     // inner place !known_valid. `exhaustive_patterns` ignores this.
     let Ok(_x) = &res_u32_never;
-    //[normal,min_exh_pats]~^ ERROR refutable
+    //[normal,min_exh_pats,never_pats]~^ ERROR refutable
 
     let result_never: Result = x.result_never;
     match result_never {}
-    //[normal]~^ ERROR non-exhaustive
+    //[normal,never_pats]~^ ERROR non-exhaustive
     match result_never {
         _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
     }
     match result_never {
-        //[normal]~^ ERROR non-exhaustive
+        //[normal,never_pats]~^ ERROR non-exhaustive
         Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
     }
     match result_never {
@@ -142,7 +144,7 @@ fn void_same_as_never(x: NeverBundle) {
         }
         let opt_void: Option = None;
         match opt_void {
-            //[normal]~^ ERROR non-exhaustive
+            //[normal,never_pats]~^ ERROR non-exhaustive
             None => {}
         }
         match opt_void {
@@ -161,7 +163,7 @@ fn void_same_as_never(x: NeverBundle) {
         }
         let ref_opt_void: &Option = &None;
         match *ref_opt_void {
-            //[normal,min_exh_pats]~^ ERROR non-exhaustive
+            //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
             None => {}
         }
         match *ref_opt_void {
@@ -311,13 +313,13 @@ fn invalid_empty_match(bundle: NeverBundle) {
     match *x {}
 
     let x: &(u32, !) = &bundle.tuple_half_never;
-    match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
+    match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
     let x: &(!, !) = &bundle.tuple_never;
-    match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
+    match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
     let x: &Result = &bundle.result_never;
-    match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
+    match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
     let x: &[!; 3] = &bundle.array_3_never;
-    match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
+    match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
 }
 
 fn arrays_and_slices(x: NeverBundle) {
@@ -325,7 +327,7 @@ fn arrays_and_slices(x: NeverBundle) {
     match slice_never {}
     //~^ ERROR non-empty
     match slice_never {
-        //[normal,min_exh_pats]~^ ERROR not covered
+        //[normal,min_exh_pats,never_pats]~^ ERROR not covered
         [] => {}
     }
     match slice_never {
@@ -336,6 +338,7 @@ fn arrays_and_slices(x: NeverBundle) {
     match slice_never {
         //[normal,min_exh_pats]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered
         //[exhaustive_patterns]~^^ ERROR `&[]` not covered
+        //[never_pats]~^^^ ERROR `&[]`, `&[!]` and `&[!, !]` not covered
         [_, _, _, ..] => {}
     }
     match slice_never {
@@ -349,6 +352,7 @@ fn arrays_and_slices(x: NeverBundle) {
     match slice_never {
         //[normal,min_exh_pats]~^ ERROR `&[]` and `&[_, ..]` not covered
         //[exhaustive_patterns]~^^ ERROR `&[]` not covered
+        //[never_pats]~^^^ ERROR `&[]` and `&[!, ..]` not covered
         &[..] if false => {}
     }
 
@@ -360,7 +364,7 @@ fn arrays_and_slices(x: NeverBundle) {
 
     let array_3_never: [!; 3] = x.array_3_never;
     match array_3_never {}
-    //[normal]~^ ERROR non-empty
+    //[normal,never_pats]~^ ERROR non-empty
     match array_3_never {
         _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
     }
@@ -446,7 +450,7 @@ fn bindings(x: NeverBundle) {
         &_a => {}
     }
     match ref_opt_never {
-        //[normal,min_exh_pats]~^ ERROR non-exhaustive
+        //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
         &None => {}
     }
     match ref_opt_never {
@@ -487,7 +491,7 @@ fn bindings(x: NeverBundle) {
         ref _a => {}
     }
     match *ref_opt_never {
-        //[normal,min_exh_pats]~^ ERROR non-exhaustive
+        //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
         None => {}
     }
     match *ref_opt_never {
@@ -535,7 +539,7 @@ fn bindings(x: NeverBundle) {
 
     let ref_res_never: &Result = &x.result_never;
     match *ref_res_never {
-        //[normal,min_exh_pats]~^ ERROR non-exhaustive
+        //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
         // useful, reachable
         Ok(_) => {}
     }
@@ -546,7 +550,7 @@ fn bindings(x: NeverBundle) {
         _ => {}
     }
     match *ref_res_never {
-        //[normal,min_exh_pats]~^ ERROR non-exhaustive
+        //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
         // useful, !reachable
         Ok(_a) => {}
     }
@@ -565,7 +569,7 @@ fn bindings(x: NeverBundle) {
 
     let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never;
     match *ref_tuple_half_never {}
-    //[normal,min_exh_pats]~^ ERROR non-empty
+    //[normal,min_exh_pats,never_pats]~^ ERROR non-empty
     match *ref_tuple_half_never {
         // useful, reachable
         (_, _) => {}
@@ -632,7 +636,7 @@ fn guards_and_validity(x: NeverBundle) {
         _a if false => {}
     }
     match ref_never {
-        //[normal,min_exh_pats]~^ ERROR non-exhaustive
+        //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
         // useful, !reachable
         &_a if false => {}
     }
@@ -647,6 +651,14 @@ fn guards_and_validity(x: NeverBundle) {
         // useful, !reachable
         Err(_) => {}
     }
+    match *ref_result_never {
+        //[normal,min_exh_pats]~^ ERROR `Ok(_)` not covered
+        //[never_pats]~^^ ERROR `Ok(!)` not covered
+        // useful, reachable
+        Ok(_) if false => {}
+        // useful, reachable
+        Err(_) => {}
+    }
     let ref_tuple_never: &(!, !) = &x.tuple_never;
     match *ref_tuple_never {
         // useful, !reachable
@@ -661,6 +673,7 @@ fn diagnostics_subtlety(x: NeverBundle) {
     let x: &Option> = &None;
     match *x {
         //[normal,min_exh_pats]~^ ERROR `Some(_)` not covered
+        //[never_pats]~^^ ERROR `Some(!)` not covered
         None => {}
     }
 }
diff --git a/tests/ui/resolve/issue-39559-2.stderr b/tests/ui/resolve/issue-39559-2.stderr
index e9d8eb0835b..7f51357a56f 100644
--- a/tests/ui/resolve/issue-39559-2.stderr
+++ b/tests/ui/resolve/issue-39559-2.stderr
@@ -5,7 +5,10 @@ LL |     let array: [usize; Dim3::dim()]
    |                        ^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const fn `::dim` in constants
   --> $DIR/issue-39559-2.rs:16:15
@@ -14,7 +17,10 @@ LL |         = [0; Dim3::dim()];
    |               ^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/check.rs b/tests/ui/rfcs/rfc-0000-never_patterns/check.rs
index b6da0c20e07..0831477e749 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/check.rs
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/check.rs
@@ -15,12 +15,12 @@ fn no_arms_or_guards(x: Void) {
         //~^ ERROR a never pattern is always unreachable
         None => {}
     }
-    match None:: { //~ ERROR: `Some(_)` not covered
+    match None:: { //~ ERROR: `Some(!)` not covered
         Some(!) if true,
         //~^ ERROR guard on a never pattern
         None => {}
     }
-    match None:: { //~ ERROR: `Some(_)` not covered
+    match None:: { //~ ERROR: `Some(!)` not covered
         Some(!) if true => {}
         //~^ ERROR a never pattern is always unreachable
         None => {}
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr
index 5497252890f..25f7343a8a8 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr
@@ -31,11 +31,11 @@ LL |         Some(never!()) => {}
    |                           this will never be executed
    |                           help: remove this expression
 
-error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+error[E0004]: non-exhaustive patterns: `Some(!)` not covered
   --> $DIR/check.rs:18:11
    |
 LL |     match None:: {
-   |           ^^^^^^^^^^^^ pattern `Some(_)` not covered
+   |           ^^^^^^^^^^^^ pattern `Some(!)` not covered
    |
 note: `Option` defined here
   --> $SRC_DIR/core/src/option.rs:LL:COL
@@ -46,14 +46,14 @@ note: `Option` defined here
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL ~         None => {},
-LL +         Some(_) => todo!()
+LL +         Some(!)
    |
 
-error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+error[E0004]: non-exhaustive patterns: `Some(!)` not covered
   --> $DIR/check.rs:23:11
    |
 LL |     match None:: {
-   |           ^^^^^^^^^^^^ pattern `Some(_)` not covered
+   |           ^^^^^^^^^^^^ pattern `Some(!)` not covered
    |
 note: `Option` defined here
   --> $SRC_DIR/core/src/option.rs:LL:COL
@@ -64,7 +64,7 @@ note: `Option` defined here
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL ~         None => {},
-LL +         Some(_) => todo!()
+LL +         Some(!)
    |
 
 error: aborting due to 6 previous errors
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr
index f802841d2e4..22e8e692752 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr
@@ -10,7 +10,10 @@ note: impl defined here, but it is not `const`
 LL | impl const std::ops::Add for Int {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const fn `::plus` in constant functions
   --> $DIR/call-const-trait-method-pass.rs:36:7
@@ -19,7 +22,10 @@ LL |     a.plus(b)
    |       ^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr
index 14c41f3e01d..151bd6facf7 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr
@@ -11,11 +11,14 @@ LL |     x(())
    |     ^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL | const fn need_const_closure i32 + ~const std::ops::FnOnce<((),)>>(x: T) -> i32 {
    |                                                         ++++++++++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr
index 8fe11fffbf9..e2b3e352701 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr
@@ -11,11 +11,14 @@ LL |     x(())
    |     ^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL | const fn need_const_closure i32 + ~const std::ops::FnOnce<((),)>>(x: T) -> i32 {
    |                                                         ++++++++++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr
index a225125ef53..e5a773123e9 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr
@@ -29,11 +29,14 @@ LL |     f() + f()
    |     ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL | const fn answer u8 + ~const std::ops::Fn<()>>(f: &F) -> u8 {
    |                                      +++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const closure in constant functions
   --> $DIR/const-closures.rs:24:11
@@ -42,11 +45,14 @@ LL |     f() + f()
    |           ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL | const fn answer u8 + ~const std::ops::Fn<()>>(f: &F) -> u8 {
    |                                      +++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const closure in constant functions
   --> $DIR/const-closures.rs:12:5
@@ -55,11 +61,14 @@ LL |     f() * 7
    |     ^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
 help: consider further restricting this bound
    |
 LL |         F: ~const FnOnce() -> u8 + ~const std::ops::Fn<()>,
    |                                  +++++++++++++++++++++++++
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs
index 91f1b90bdc0..51dfe29b829 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs
@@ -3,7 +3,6 @@
 
 #![allow(incomplete_features)]
 #![feature(
-    associated_type_bounds,
     const_trait_impl,
     effects,
     const_cmp,
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr
index d4be71f2f46..03038eb5c84 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr
@@ -1,5 +1,5 @@
 error[E0277]: can't compare `()` with `()`
-  --> $DIR/const-impl-trait.rs:37:17
+  --> $DIR/const-impl-trait.rs:36:17
    |
 LL |     assert!(cmp(&()));
    |             --- ^^^ no implementation for `() == ()`
@@ -9,13 +9,13 @@ LL |     assert!(cmp(&()));
    = help: the trait `const PartialEq` is not implemented for `()`
    = help: the trait `PartialEq` is implemented for `()`
 note: required by a bound in `cmp`
-  --> $DIR/const-impl-trait.rs:14:23
+  --> $DIR/const-impl-trait.rs:13:23
    |
 LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
    |                       ^^^^^^^^^^^^^^^^ required by this bound in `cmp`
 
 error[E0369]: binary operation `==` cannot be applied to type `&impl ~const PartialEq`
-  --> $DIR/const-impl-trait.rs:15:7
+  --> $DIR/const-impl-trait.rs:14:7
    |
 LL |     a == a
    |     - ^^ - &impl ~const PartialEq
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr
index ab039397edc..a0c50ac7e61 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr
@@ -5,7 +5,10 @@ LL |     Const.func();
    |           ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr
index ebbe9aa2202..312818ae631 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr
@@ -5,7 +5,10 @@ LL |     NonConst.func();
    |              ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const fn `::func` in constant functions
   --> $DIR/cross-crate.rs:20:11
@@ -14,7 +17,10 @@ LL |     Const.func();
    |           ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr
index f42fee59bf0..7905abfa40e 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr
@@ -10,7 +10,10 @@ note: impl defined here, but it is not `const`
 LL | impl const std::ops::Add for S {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr
index 0fa4c8fe04c..8401d1bd4f6 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr
@@ -6,7 +6,10 @@ LL |         n => n(),
    |
    = note: closures need an RFC before allowed to be called in constants
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr
index e5347a09598..afe1ea3b1b7 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr
@@ -5,7 +5,10 @@ LL |     T::assoc()
    |     ^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr
index 28254ac15a8..c7d21151661 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.gated.stderr
@@ -6,7 +6,10 @@ LL |         "a" => (), //FIXME [gated]~ ERROR can't compare `str` with `str` in
    |
    = note: `str` cannot be compared in compile-time, and therefore cannot be used in `match`es
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr
index 5431116a1a7..0f5ecac3891 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/match-non-const-eq.stock.stderr
@@ -6,7 +6,10 @@ LL |         "a" => (), //FIXME [gated]~ ERROR can't compare `str` with `str` in
    |
    = note: `str` cannot be compared in compile-time, and therefore cannot be used in `match`es
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr
index d82a49be75e..c362a1077e3 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr
@@ -5,7 +5,10 @@ LL |     (const || { (()).foo() })();
    |                      ^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr
index 1346c4c4ae2..781191ec97c 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr
@@ -5,7 +5,10 @@ LL |     Unstable::func();
    |     ^^^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr
index e51ff148339..d761fdce4bf 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr
@@ -11,7 +11,10 @@ LL |     Default::default()
    |     ^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr
index 6d624def276..b63ea695fc2 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr
@@ -5,7 +5,10 @@ LL |     Default::default()
    |     ^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/specialization/const_trait_impl.stderr b/tests/ui/specialization/const_trait_impl.stderr
index 187049e623c..fc02f6f8f74 100644
--- a/tests/ui/specialization/const_trait_impl.stderr
+++ b/tests/ui/specialization/const_trait_impl.stderr
@@ -23,7 +23,10 @@ LL | const _: () = assert!(<()>::a() == 42);
    |                       ^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const fn `::a` in constants
   --> $DIR/const_trait_impl.rs:53:23
@@ -32,7 +35,10 @@ LL | const _: () = assert!(::a() == 3);
    |                       ^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error[E0015]: cannot call non-const fn `::a` in constants
   --> $DIR/const_trait_impl.rs:54:23
@@ -41,7 +47,10 @@ LL | const _: () = assert!(::a() == 2);
    |                       ^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(effects)]` to the crate attributes to enable
+help: add `#![feature(effects)]` to the crate attributes to enable
+   |
+LL + #![feature(effects)]
+   |
 
 error: aborting due to 6 previous errors
 
diff --git a/tests/ui/specialization/defaultimpl/validation.stderr b/tests/ui/specialization/defaultimpl/validation.stderr
index 5f62e8dce17..f56f16162a2 100644
--- a/tests/ui/specialization/defaultimpl/validation.stderr
+++ b/tests/ui/specialization/defaultimpl/validation.stderr
@@ -35,7 +35,10 @@ LL | default unsafe impl Send for S {}
    = help: the trait `Send` is not implemented for `S`
    = help: the trait `Send` is implemented for `S`
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error: impls of auto traits cannot be default
   --> $DIR/validation.rs:11:15
diff --git a/tests/ui/suggestions/missing-assoc-fn.rs b/tests/ui/suggestions/missing-assoc-fn.rs
index 9af8e5a939d..260d3b33e28 100644
--- a/tests/ui/suggestions/missing-assoc-fn.rs
+++ b/tests/ui/suggestions/missing-assoc-fn.rs
@@ -6,7 +6,7 @@ trait TraitA {
     fn foo>(_: T) -> Self;
     fn bar(_: T) -> Self;
     fn baz(_: T) -> Self where T: TraitB, ::Item: Copy;
-    fn bat>(_: T) -> Self; //~ ERROR associated type bounds are unstable
+    fn bat>(_: T) -> Self;
 }
 
 struct S;
diff --git a/tests/ui/suggestions/missing-assoc-fn.stderr b/tests/ui/suggestions/missing-assoc-fn.stderr
index 61a5492d583..d819f7e8bd2 100644
--- a/tests/ui/suggestions/missing-assoc-fn.stderr
+++ b/tests/ui/suggestions/missing-assoc-fn.stderr
@@ -1,13 +1,3 @@
-error[E0658]: associated type bounds are unstable
-  --> $DIR/missing-assoc-fn.rs:9:22
-   |
-LL |     fn bat>(_: T) -> Self;
-   |                      ^^^^^^^^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz`, `bat`
   --> $DIR/missing-assoc-fn.rs:14:1
    |
@@ -31,7 +21,6 @@ LL | impl FromIterator<()> for X {
    |
    = help: implement the missing item: `fn from_iter>(_: T) -> Self { todo!() }`
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0046, E0658.
-For more information about an error, try `rustc --explain E0046`.
+For more information about this error, try `rustc --explain E0046`.
diff --git a/tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs
new file mode 100644
index 00000000000..135535cd00a
--- /dev/null
+++ b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.rs
@@ -0,0 +1,20 @@
+pub struct Struct {
+    a: Vec,
+}
+
+impl Struct {
+    pub fn test(&self) {
+        if let [Struct { a: [] }] = &self.a {
+            //~^ ERROR expected an array or slice
+            //~| ERROR expected an array or slice
+            println!("matches!")
+        }
+
+        if let [Struct { a: [] }] = &self.a[..] {
+            //~^ ERROR expected an array or slice
+            println!("matches!")
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr
new file mode 100644
index 00000000000..c28d67604da
--- /dev/null
+++ b/tests/ui/suggestions/suppress-consider-slicing-issue-120605.stderr
@@ -0,0 +1,23 @@
+error[E0529]: expected an array or slice, found `Vec`
+  --> $DIR/suppress-consider-slicing-issue-120605.rs:7:16
+   |
+LL |         if let [Struct { a: [] }] = &self.a {
+   |                ^^^^^^^^^^^^^^^^^^   ------- help: consider slicing here: `&self.a[..]`
+   |                |
+   |                pattern cannot match with input type `Vec`
+
+error[E0529]: expected an array or slice, found `Vec`
+  --> $DIR/suppress-consider-slicing-issue-120605.rs:7:29
+   |
+LL |         if let [Struct { a: [] }] = &self.a {
+   |                             ^^ pattern cannot match with input type `Vec`
+
+error[E0529]: expected an array or slice, found `Vec`
+  --> $DIR/suppress-consider-slicing-issue-120605.rs:13:29
+   |
+LL |         if let [Struct { a: [] }] = &self.a[..] {
+   |                             ^^ pattern cannot match with input type `Vec`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0529`.
diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs
index 48d19f6dd4e..c98eec4af01 100644
--- a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs
+++ b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs
@@ -6,8 +6,6 @@ fn main() {
     let _: Vec = A::B;
     //~^ ERROR cannot find trait `B` in this scope
     //~| HELP you might have meant to write a path instead of an associated type bound
-    //~| ERROR associated type bounds are unstable
-    //~| HELP add `#![feature(associated_type_bounds)]` to the crate attributes to enable
     //~| ERROR struct takes at least 1 generic argument but 0 generic arguments were supplied
     //~| HELP add missing generic argument
     //~| ERROR associated type bindings are not allowed here
diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr
index 9c22873e79c..834c141ec3e 100644
--- a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr
+++ b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr
@@ -9,16 +9,6 @@ help: you might have meant to write a path instead of an associated type bound
 LL |     let _: Vec = A::B;
    |                 ~~
 
-error[E0658]: associated type bounds are unstable
-  --> $DIR/type-ascription-instead-of-path-in-type.rs:6:16
-   |
-LL |     let _: Vec = A::B;
-   |                ^^^
-   |
-   = note: see issue #52662  for more information
-   = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error[E0107]: struct takes at least 1 generic argument but 0 generic arguments were supplied
   --> $DIR/type-ascription-instead-of-path-in-type.rs:6:12
    |
@@ -36,7 +26,7 @@ error[E0229]: associated type bindings are not allowed here
 LL |     let _: Vec = A::B;
    |                ^^^ associated type not allowed here
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0107, E0229, E0405, E0658.
+Some errors have detailed explanations: E0107, E0229, E0405.
 For more information about an error, try `rustc --explain E0107`.
diff --git a/tests/ui/trait-bounds/super-assoc-mismatch.stderr b/tests/ui/trait-bounds/super-assoc-mismatch.stderr
index 47535776348..f2c5eb47e59 100644
--- a/tests/ui/trait-bounds/super-assoc-mismatch.stderr
+++ b/tests/ui/trait-bounds/super-assoc-mismatch.stderr
@@ -78,7 +78,10 @@ help: this trait has no implementations, consider adding one
 LL | trait Sub: Super {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: see issue #48214
-   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+   |
+LL + #![feature(trivial_bounds)]
+   |
 
 error[E0277]: the trait bound `(): SubGeneric` is not satisfied
   --> $DIR/super-assoc-mismatch.rs:55:22
diff --git a/tests/ui/traits/negative-bounds/associated-constraints.rs b/tests/ui/traits/negative-bounds/associated-constraints.rs
index 4a7132ccde9..795e68bb469 100644
--- a/tests/ui/traits/negative-bounds/associated-constraints.rs
+++ b/tests/ui/traits/negative-bounds/associated-constraints.rs
@@ -1,4 +1,4 @@
-#![feature(negative_bounds, associated_type_bounds)]
+#![feature(negative_bounds)]
 
 trait Trait {
     type Assoc;
diff --git a/tests/ui/traits/next-solver/coherence-fulfill-overflow.rs b/tests/ui/traits/next-solver/coherence/coherence-fulfill-overflow.rs
similarity index 100%
rename from tests/ui/traits/next-solver/coherence-fulfill-overflow.rs
rename to tests/ui/traits/next-solver/coherence/coherence-fulfill-overflow.rs
diff --git a/tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr b/tests/ui/traits/next-solver/coherence/coherence-fulfill-overflow.stderr
similarity index 100%
rename from tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr
rename to tests/ui/traits/next-solver/coherence/coherence-fulfill-overflow.stderr
diff --git a/tests/ui/traits/next-solver/negative-coherence-bounds.rs b/tests/ui/traits/next-solver/coherence/negative-coherence-bounds.rs
similarity index 100%
rename from tests/ui/traits/next-solver/negative-coherence-bounds.rs
rename to tests/ui/traits/next-solver/coherence/negative-coherence-bounds.rs
diff --git a/tests/ui/traits/next-solver/negative-coherence-bounds.stderr b/tests/ui/traits/next-solver/coherence/negative-coherence-bounds.stderr
similarity index 100%
rename from tests/ui/traits/next-solver/negative-coherence-bounds.stderr
rename to tests/ui/traits/next-solver/coherence/negative-coherence-bounds.stderr
diff --git a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr
index 39d453e8035..1d42dbdfe00 100644
--- a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr
+++ b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Sized
 LL |     type Assoc = ::Assoc;
    |                  ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`)
 note: required by a bound in `Overflow::Assoc`
   --> $DIR/trait_ref_is_knowable-norm-overflow.rs:7:5
    |
@@ -23,9 +22,6 @@ LL | impl Trait for T {}
 LL | struct LocalTy;
 LL | impl Trait for ::Assoc {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
-   |
-   = note: overflow evaluating the requirement `_ == ::Assoc`
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/traits/next-solver/equating-projection-cyclically.rs b/tests/ui/traits/next-solver/generalize/equating-projection-cyclically.rs
similarity index 100%
rename from tests/ui/traits/next-solver/equating-projection-cyclically.rs
rename to tests/ui/traits/next-solver/generalize/equating-projection-cyclically.rs
diff --git a/tests/ui/traits/next-solver/normalize/indirectly-constrained-term.rs b/tests/ui/traits/next-solver/normalize/indirectly-constrained-term.rs
new file mode 100644
index 00000000000..380477c2c3c
--- /dev/null
+++ b/tests/ui/traits/next-solver/normalize/indirectly-constrained-term.rs
@@ -0,0 +1,45 @@
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver=coherence
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@ check-pass
+
+// A regression test for `paperclip-core`. This previously failed to compile
+// in the new solver.
+//
+// Behavior in old solver:
+// We prove `Projection( as Unconstrained>::Assoc, ())`. This
+// normalizes ` as Unconstrained>::Assoc` to `?1` with nested goals
+// `[Projection(::Assoc, ?1), Trait(?1: NoImpl)]`.
+// We then unify `?1` with `()`. At this point `?1: NoImpl` does not hold,
+// and we get an error.
+//
+// Previous behavior of the new solver:
+// We prove `Projection( as Unconstrained>::Assoc, ())`. This normalizes
+// ` as Unconstrained>::Assoc` to `?1` and eagerly computes the nested
+// goals `[Projection(::Assoc, ?1), Trait(?1: NoImpl)]`.
+// These goals are both ambiguous. `NormalizesTo`` then returns `?1` as the
+// normalized-to type. It discards the nested goals, forcing the certainty of
+// the normalization to `Maybe`. Unifying `?1` with `()` succeeds¹. However,
+// this is never propagated to the `?1: NoImpl` goal, as it only exists inside
+// of the `NormalizesTo` goal. The normalized-to term always starts out as
+// unconstrained.
+//
+// We fix this regression by returning the nested goals of `NormalizesTo` goals
+// to the `AliasRelate`. This results in us checking `(): NoImpl`, same as the
+// old solver.
+
+struct W(T);
+trait NoImpl {}
+trait Unconstrained {
+    type Assoc;
+}
+impl, U: NoImpl> Unconstrained for W {
+    type Assoc = U;
+}
+
+
+trait Overlap {}
+impl> Overlap for T {}
+impl Overlap for W {}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs
similarity index 100%
rename from tests/ui/traits/next-solver/two-projection-param-candidates-are-ambiguous.rs
rename to tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs
diff --git a/tests/ui/traits/next-solver/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr
similarity index 100%
rename from tests/ui/traits/next-solver/two-projection-param-candidates-are-ambiguous.stderr
rename to tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr
diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr
index 09622bb9b6c..2b0e57966fe 100644
--- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr
+++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr
@@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc1 == _`
    |
 LL |     needs_bar::();
    |                 ^^^^^^^^^
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
 
 error[E0275]: overflow evaluating the requirement `::Assoc1: Bar`
   --> $DIR/recursive-self-normalization-2.rs:15:17
@@ -12,7 +10,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc1: Bar`
 LL |     needs_bar::();
    |                 ^^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
 note: required by a bound in `needs_bar`
   --> $DIR/recursive-self-normalization-2.rs:12:17
    |
@@ -25,7 +22,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc1: Sized`
 LL |     needs_bar::();
    |                 ^^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
 note: required by an implicit `Sized` bound in `needs_bar`
   --> $DIR/recursive-self-normalization-2.rs:12:14
    |
@@ -41,8 +37,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc1 == _`
    |
 LL |     needs_bar::();
    |     ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
 
 error[E0275]: overflow evaluating the requirement `::Assoc1 == _`
   --> $DIR/recursive-self-normalization-2.rs:15:5
@@ -50,7 +44,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc1 == _`
 LL |     needs_bar::();
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0275]: overflow evaluating the requirement `::Assoc1 == _`
@@ -59,7 +52,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc1 == _`
 LL |     needs_bar::();
    |                 ^^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: aborting due to 6 previous errors
diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr
index 7c058909df7..af8504dcaee 100644
--- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr
+++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr
@@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc == _`
    |
 LL |     needs_bar::();
    |                 ^^^^^^^^
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
 
 error[E0275]: overflow evaluating the requirement `::Assoc: Bar`
   --> $DIR/recursive-self-normalization.rs:11:17
@@ -12,7 +10,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Bar`
 LL |     needs_bar::();
    |                 ^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
 note: required by a bound in `needs_bar`
   --> $DIR/recursive-self-normalization.rs:8:17
    |
@@ -25,7 +22,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Sized`
 LL |     needs_bar::();
    |                 ^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
 note: required by an implicit `Sized` bound in `needs_bar`
   --> $DIR/recursive-self-normalization.rs:8:14
    |
@@ -41,8 +37,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc == _`
    |
 LL |     needs_bar::();
    |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
 
 error[E0275]: overflow evaluating the requirement `::Assoc == _`
   --> $DIR/recursive-self-normalization.rs:11:5
@@ -50,7 +44,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc == _`
 LL |     needs_bar::();
    |     ^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0275]: overflow evaluating the requirement `::Assoc == _`
@@ -59,7 +52,6 @@ error[E0275]: overflow evaluating the requirement `::Assoc == _`
 LL |     needs_bar::();
    |                 ^^^^^^^^
    |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: aborting due to 6 previous errors
diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr
index e8f1de1ad04..8bcad56916a 100644
--- a/tests/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr
@@ -673,7 +673,10 @@ LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
    |                      ^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0015]: cannot call non-const fn `, {closure@$DIR/typeck_type_placeholder_item.rs:230:29: 230:32}> as Iterator>::map::` in constants
   --> $DIR/typeck_type_placeholder_item.rs:230:45
@@ -682,7 +685,10 @@ LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x);
    |                                             ^^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
 
 error[E0515]: cannot return reference to function parameter `x`
   --> $DIR/typeck_type_placeholder_item.rs:50:5
diff --git a/triagebot.toml b/triagebot.toml
index 89ffb2f41bb..0a36eab7b87 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -515,6 +515,9 @@ cc = ["@Nadrieril"]
 message = "Some changes occurred in exhaustiveness checking"
 cc = ["@Nadrieril"]
 
+[mentions."compiler/rustc_lint/src/context/diagnostics/check_cfg.rs"]
+cc = ["@Urgau"]
+
 [mentions."library/core/src/intrinsics/simd.rs"]
 message = """
 Some changes occurred to the platform-builtins intrinsics. Make sure the
@@ -699,6 +702,9 @@ cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"]
 [mentions."src/doc/unstable-book/src/language-features/no-sanitize.md"]
 cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"]
 
+[mentions."src/doc/unstable-book/src/compiler-flags/check-cfg.md"]
+cc = ["@Urgau"]
+
 [mentions."tests/codegen/sanitizer"]
 cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"]
 
@@ -717,6 +723,33 @@ cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"]
 [mentions."tests/ui/stack-protector"]
 cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"]
 
+[mentions."tests/ui/check-cfg"]
+cc = ["@Urgau"]
+
+[mentions."compiler/rustc_middle/src/mir/coverage.rs"]
+message = "Some changes occurred in coverage instrumentation."
+cc = ["@Zalathar"]
+
+[mentions."compiler/rustc_mir_build/src/build/coverageinfo.rs"]
+message = "Some changes occurred in coverage instrumentation."
+cc = ["@Zalathar"]
+
+[mentions."compiler/rustc_mir_transform/src/coverage"]
+message = "Some changes occurred in coverage instrumentation."
+cc = ["@Zalathar"]
+
+[mentions."compiler/rustc_codegen_llvm/src/coverageinfo"]
+message = "Some changes occurred in coverage instrumentation."
+cc = ["@Zalathar"]
+
+[mentions."compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs"]
+message = "Some changes occurred in coverage instrumentation."
+cc = ["@Zalathar"]
+
+[mentions."tests/coverage"]
+message = "Some changes occurred in coverage tests."
+cc = ["@Zalathar"]
+
 [assign]
 warn_non_default_branch = true
 contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html"