diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 663ace48e9e80..9fd8054a12e46 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -23,3 +23,5 @@ b2d2184edea578109a48ec3d8decbee5948e8f35 # test directives migration 6e48b96692d63a79a14563f27fe5185f122434f8 ec2cc761bc7067712ecc7734502f703fe3b024c8 +# format use declarations +84ac80f1921afc243d71fd0caaa4f2838c294102 diff --git a/.gitignore b/.gitignore index 87d02563ed048..aabec0988a99e 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ build/ /dist/ /unicode-downloads /target +/library/target /src/bootstrap/target /src/tools/x/target # Created by default with `src/ci/docker/run.sh` diff --git a/.gitmodules b/.gitmodules index 9ad207a0d5226..b5250d493864e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,7 +33,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/18.1-2024-05-19 + branch = rustc/19.1-2024-07-30 shallow = true [submodule "src/doc/embedded-book"] path = src/doc/embedded-book diff --git a/Cargo.lock b/Cargo.lock index 1a7d7e3f5d7d1..a4b4e49f82c2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,27 +11,11 @@ dependencies = [ "gimli 0.28.1", ] -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "compiler_builtins", - "gimli 0.29.0", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] [[package]] name = "aes" @@ -66,16 +50,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alloc" -version = "0.0.0" -dependencies = [ - "compiler_builtins", - "core", - "rand", - "rand_xorshift", -] - [[package]] name = "allocator-api2" version = "0.2.18" @@ -256,7 +230,7 @@ version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ - "addr2line 0.21.0", + "addr2line", "cc", "cfg-if", "libc", @@ -455,10 +429,6 @@ name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] [[package]] name = "cfg_aliases" @@ -737,16 +707,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" -[[package]] -name = "compiler_builtins" -version = "0.1.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb58b199190fcfe0846f55a3b545cd6b07a34bdd5930a476ff856f3ebcc5558a" -dependencies = [ - "cc", - "rustc-std-workspace-core", -] - [[package]] name = "compiletest" version = "0.0.0" @@ -771,7 +731,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows", + "windows 0.52.0", ] [[package]] @@ -787,14 +747,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "core" -version = "0.0.0" -dependencies = [ - "rand", - "rand_xorshift", -] - [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -1134,19 +1086,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" -[[package]] -name = "dlmalloc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d" -dependencies = [ - "cfg-if", - "compiler_builtins", - "libc", - "rustc-std-workspace-core", - "windows-sys 0.52.0", -] - [[package]] name = "either" version = "1.12.0" @@ -1348,16 +1287,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fortanix-sgx-abi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - [[package]] name = "fs-err" version = "2.11.0" @@ -1504,8 +1433,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "rustc-std-workspace-core", - "rustc-std-workspace-std", "unicode-width", ] @@ -1526,25 +1453,11 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ - "compiler_builtins", "fallible-iterator", "indexmap", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", "stable_deref_trait", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - [[package]] name = "gimli" version = "0.31.0" @@ -1606,9 +1519,6 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", "serde", ] @@ -1630,17 +1540,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - [[package]] name = "hex" version = "0.4.3" @@ -1713,7 +1612,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -2065,9 +1964,6 @@ name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -dependencies = [ - "rustc-std-workspace-core", -] [[package]] name = "libdbus-sys" @@ -2299,10 +2195,6 @@ name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] [[package]] name = "memmap2" @@ -2357,9 +2249,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", ] [[package]] @@ -2393,6 +2282,7 @@ dependencies = [ "smallvec", "tempfile", "ui_test 0.21.2", + "windows-sys 0.52.0", ] [[package]] @@ -2436,15 +2326,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2549,7 +2430,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -2588,14 +2469,11 @@ version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ - "compiler_builtins", "crc32fast", "flate2", "hashbrown", "indexmap", "memchr", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", "ruzstd 0.7.0", "wasmparser 0.214.0", ] @@ -2719,29 +2597,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "panic_abort" -version = "0.0.0" -dependencies = [ - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "libc", -] - -[[package]] -name = "panic_unwind" -version = "0.0.0" -dependencies = [ - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "libc", - "unwind", -] - [[package]] name = "papergrid" version = "0.11.0" @@ -2996,23 +2851,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc_macro" -version = "0.0.0" -dependencies = [ - "core", - "std", -] - -[[package]] -name = "profiler_builtins" -version = "0.0.0" -dependencies = [ - "cc", - "compiler_builtins", - "core", -] - [[package]] name = "psm" version = "0.1.21" @@ -3090,27 +2928,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "r-efi-alloc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" -dependencies = [ - "compiler_builtins", - "r-efi", - "rustc-std-workspace-core", -] - [[package]] name = "rand" version = "0.8.5" @@ -3141,15 +2958,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - [[package]] name = "rand_xoshiro" version = "0.6.0" @@ -3291,20 +3099,22 @@ dependencies = [ [[package]] name = "rinja" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d47a46d7729e891c8accf260e9daa02ae6d570aa2a94fb1fb27eb5364a2323" +checksum = "6d3762e3740cdbf2fd2be465cc2c26d643ad17353cc2e0223d211c1b096118bd" dependencies = [ + "itoa", "rinja_derive", ] [[package]] name = "rinja_derive" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dae9afe59d58ed8d988d67d1945f3638125d2fd2104058399382e11bd3ea2a" +checksum = "fd01fd8e15e7d19c8b8052c1d428325131e02ff1633cdcf695190c2e56ab682c" dependencies = [ "basic-toml", + "memchr", "mime", "mime_guess", "once_map", @@ -3317,10 +3127,11 @@ dependencies = [ [[package]] name = "rinja_parser" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1771c78cd5d3b1646ef8d8f2ed100db936e8b291d3cc06e92a339ff346858c" +checksum = "a2f6bf7cef118c6de21206edf0b3f19f5ede60006be674a58ca21b6e003a1b57" dependencies = [ + "memchr", "nom", ] @@ -3346,9 +3157,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb" +checksum = "2471f8f296262437d7e848e527b4210b44a96e53a3b4435b890227ce3e6da106" dependencies = [ "anyhow", "rustc_version", @@ -3361,10 +3172,6 @@ name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] [[package]] name = "rustc-hash" @@ -3425,27 +3232,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5c9f15eec8235d7cb775ee6f81891db79b98fd54ba1ad8fae565b88ef1ae4e2" -[[package]] -name = "rustc-std-workspace-alloc" -version = "1.99.0" -dependencies = [ - "alloc", -] - -[[package]] -name = "rustc-std-workspace-core" -version = "1.99.0" -dependencies = [ - "core", -] - -[[package]] -name = "rustc-std-workspace-std" -version = "1.99.0" -dependencies = [ - "std", -] - [[package]] name = "rustc_abi" version = "0.0.0" @@ -3717,7 +3503,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.210.0", - "windows", + "windows 0.52.0", ] [[package]] @@ -3774,7 +3560,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows", + "windows 0.52.0", ] [[package]] @@ -3835,7 +3621,7 @@ dependencies = [ "shlex", "time", "tracing", - "windows", + "windows 0.52.0", ] [[package]] @@ -3886,7 +3672,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows", + "windows 0.52.0", ] [[package]] @@ -4355,6 +4141,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", + "rustc_type_ir", "smallvec", "tracing", ] @@ -4607,7 +4394,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows", + "windows 0.52.0", ] [[package]] @@ -5231,46 +5018,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "std" -version = "0.0.0" -dependencies = [ - "addr2line 0.22.0", - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "dlmalloc", - "fortanix-sgx-abi", - "hashbrown", - "hermit-abi 0.4.0", - "libc", - "miniz_oxide", - "object 0.36.2", - "panic_abort", - "panic_unwind", - "profiler_builtins", - "r-efi", - "r-efi-alloc", - "rand", - "rand_xorshift", - "rustc-demangle", - "std_detect", - "unwind", - "wasi", -] - -[[package]] -name = "std_detect" -version = "0.1.5" -dependencies = [ - "cfg-if", - "compiler_builtins", - "libc", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - [[package]] name = "string_cache" version = "0.8.7" @@ -5365,25 +5112,13 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.12" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" +checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab" dependencies = [ - "cfg-if", "core-foundation-sys", "libc", - "ntapi", - "once_cell", - "windows", -] - -[[package]] -name = "sysroot" -version = "0.0.0" -dependencies = [ - "proc_macro", - "std", - "test", + "windows 0.57.0", ] [[package]] @@ -5470,16 +5205,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "test" -version = "0.0.0" -dependencies = [ - "core", - "getopts", - "libc", - "std", -] - [[package]] name = "test-float-parse" version = "0.1.0" @@ -5978,11 +5703,6 @@ name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", - "rustc-std-workspace-std", -] [[package]] name = "unicode-xid" @@ -6007,28 +5727,6 @@ dependencies = [ "tidy", ] -[[package]] -name = "unwind" -version = "0.0.0" -dependencies = [ - "cfg-if", - "compiler_builtins", - "core", - "libc", - "unwinding", -] - -[[package]] -name = "unwinding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" -dependencies = [ - "compiler_builtins", - "gimli 0.28.1", - "rustc-std-workspace-core", -] - [[package]] name = "url" version = "2.5.2" @@ -6100,11 +5798,6 @@ name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] [[package]] name = "wasm-bindgen" @@ -6299,7 +5992,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", "windows-targets 0.52.5", ] @@ -6326,12 +6029,55 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.67", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.67", +] + [[package]] name = "windows-metadata" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e837f3c3012cfe9e7086302a93f441a7999439be1ad4c530d55d2f6d2921809" +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 178a5ab940889..131feec70abcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,6 @@ resolver = "1" members = [ "compiler/rustc", - "library/std", - "library/sysroot", "src/etc/test-float-parse", "src/rustdoc-json-types", "src/tools/build_helper", @@ -61,23 +59,8 @@ exclude = [ # not all `Cargo.toml` files are available, so we exclude the `x` binary, # so it can be invoked before the current checkout is set up. "src/tools/x", - # stdarch has its own Cargo workspace - "library/stdarch", ] -[profile.release.package.compiler_builtins] -# For compiler-builtins we always use a high number of codegen units. -# The goal here is to place every single intrinsic into its own object -# file to avoid symbol clashes with the system libgcc if possible. Note -# that this number doesn't actually produce this many object files, we -# just don't create more than this number of object files. -# -# It's a bit of a bummer that we have to pass this here, unfortunately. -# Ideally this would be specified through an env var to Cargo so Cargo -# knows how many CGUs are for this specific crate, but for now -# per-crate configuration isn't specifiable in the environment. -codegen-units = 10000 - [profile.release.package.rustc-rayon-core] # The rustc fork of Rayon has deadlock detection code which intermittently # causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227) @@ -85,19 +68,6 @@ codegen-units = 10000 # FIXME: This workaround should be removed once #90227 is fixed. overflow-checks = false -# These dependencies of the standard library implement symbolication for -# backtraces on most platforms. Their debuginfo causes both linking to be slower -# (more data to chew through) and binaries to be larger without really all that -# much benefit. This section turns them all to down to have no debuginfo which -# helps to improve link times a little bit. -[profile.release.package] -addr2line.debug = 0 -adler.debug = 0 -gimli.debug = 0 -miniz_oxide.debug = 0 -object.debug = 0 -rustc-demangle.debug = 0 - # These are very thin wrappers around executing lld with the right binary name. # Basically nothing within them can go wrong without having been explicitly logged anyway. # We ship these in every rustc tarball and even after compression they add up @@ -120,10 +90,3 @@ codegen-units = 1 # FIXME: LTO cannot be enabled for binaries in a workspace # # lto = true - -[patch.crates-io] -# See comments in `library/rustc-std-workspace-core/README.md` for what's going on -# here -rustc-std-workspace-core = { path = 'library/rustc-std-workspace-core' } -rustc-std-workspace-alloc = { path = 'library/rustc-std-workspace-alloc' } -rustc-std-workspace-std = { path = 'library/rustc-std-workspace-std' } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 378af8af50ec1..3dc548c4554a3 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -516,7 +516,7 @@ impl Size { /// Truncates `value` to `self` bits and then sign-extends it to 128 bits /// (i.e., if it is negative, fill with 1's on the left). #[inline] - pub fn sign_extend(self, value: u128) -> u128 { + pub fn sign_extend(self, value: u128) -> i128 { let size = self.bits(); if size == 0 { // Truncated until nothing is left. @@ -526,7 +526,7 @@ impl Size { let shift = 128 - size; // Shift the unsigned value to the left, then shift back to the right as signed // (essentially fills with sign bit on the left). - (((value << shift) as i128) >> shift) as u128 + ((value << shift) as i128) >> shift } /// Truncates `value` to `self` bits. @@ -544,7 +544,7 @@ impl Size { #[inline] pub fn signed_int_min(&self) -> i128 { - self.sign_extend(1_u128 << (self.bits() - 1)) as i128 + self.sign_extend(1_u128 << (self.bits() - 1)) } #[inline] diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index efcf274e51102..a353c79f12d45 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -453,11 +453,6 @@ impl<'a> AstValidator<'a> { item_span: span, block: Some(self.current_extern_span().shrink_to_lo()), }); - } else if !self.features.unsafe_extern_blocks { - self.dcx().emit_err(errors::InvalidSafetyOnExtern { - item_span: span, - block: None, - }); } } } @@ -1054,32 +1049,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> { errors::VisibilityNotPermittedNote::IndividualForeignItems, ); - if this.features.unsafe_extern_blocks { - if &Safety::Default == safety { - if item.span.at_least_rust_2024() { - this.dcx() - .emit_err(errors::MissingUnsafeOnExtern { span: item.span }); - } else { - this.lint_buffer.buffer_lint( - MISSING_UNSAFE_ON_EXTERN, - item.id, - item.span, - BuiltinLintDiag::MissingUnsafeOnExtern { - suggestion: item.span.shrink_to_lo(), - }, - ); - } + if &Safety::Default == safety { + if item.span.at_least_rust_2024() { + this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span }); + } else { + this.lint_buffer.buffer_lint( + MISSING_UNSAFE_ON_EXTERN, + item.id, + item.span, + BuiltinLintDiag::MissingUnsafeOnExtern { + suggestion: item.span.shrink_to_lo(), + }, + ); } - } else if let &Safety::Unsafe(span) = safety { - let mut diag = this - .dcx() - .create_err(errors::UnsafeItem { span, kind: "extern block" }); - rustc_session::parse::add_feature_diagnostics( - &mut diag, - self.session, - sym::unsafe_extern_blocks, - ); - diag.emit(); } if abi.is_none() { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index e99123b9b1ca1..3ceb8e0711a21 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -560,10 +560,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental"); gate_all!(global_registration, "global registration is experimental"); gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental"); - gate_all!( - unsafe_extern_blocks, - "`unsafe extern {}` blocks and `safe` keyword are experimental" - ); gate_all!(return_type_notation, "return type notation is experimental"); if !visitor.features.never_patterns { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 5c5b1239fea77..7a925705806fc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -563,11 +563,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { } = move_spans && can_suggest_clone { - self.suggest_cloning(err, ty, expr, None, Some(move_spans)); + self.suggest_cloning(err, ty, expr, Some(move_spans)); } else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone { // The place where the type moves would be misleading to suggest clone. // #121466 - self.suggest_cloning(err, ty, expr, None, Some(move_spans)); + self.suggest_cloning(err, ty, expr, Some(move_spans)); } } @@ -1229,8 +1229,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { &self, err: &mut Diag<'_>, ty: Ty<'tcx>, - mut expr: &'tcx hir::Expr<'tcx>, - mut other_expr: Option<&'tcx hir::Expr<'tcx>>, + expr: &'tcx hir::Expr<'tcx>, use_spans: Option>, ) { if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { @@ -1242,66 +1241,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { return; } - if let Some(some_other_expr) = other_expr - && let Some(parent_binop) = - self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { - if let (hir_id, hir::Node::Expr(e)) = n - && let hir::ExprKind::AssignOp(_binop, target, _arg) = e.kind - && target.hir_id == expr.hir_id - { - Some(hir_id) - } else { - None - } - }) - && let Some(other_parent_binop) = - self.infcx.tcx.hir().parent_iter(some_other_expr.hir_id).find_map(|n| { - if let (hir_id, hir::Node::Expr(expr)) = n - && let hir::ExprKind::AssignOp(..) = expr.kind - { - Some(hir_id) - } else { - None - } - }) - && parent_binop == other_parent_binop - { - // Explicitly look for `expr += other_expr;` and avoid suggesting - // `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`. - other_expr = Some(expr); - expr = some_other_expr; - } - 'outer: { - if let ty::Ref(..) = ty.kind() { - // We check for either `let binding = foo(expr, other_expr);` or - // `foo(expr, other_expr);` and if so we don't suggest an incorrect - // `foo(expr, other_expr).clone()` - if let Some(other_expr) = other_expr - && let Some(parent_let) = - self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { - if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n { - Some(hir_id) - } else { - None - } - }) - && let Some(other_parent_let) = - self.infcx.tcx.hir().parent_iter(other_expr.hir_id).find_map(|n| { - if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n { - Some(hir_id) - } else { - None - } - }) - && parent_let == other_parent_let - { - // Explicitly check that we don't have `foo(&*expr, other_expr)`, as cloning the - // result of `foo(...)` won't help. - break 'outer; - } - } - } - let ty = ty.peel_refs(); if self.implements_clone(ty) { self.suggest_cloning_inner(err, ty, expr); } else if let ty::Adt(def, args) = ty.kind() @@ -1573,10 +1512,27 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { ); self.suggest_copy_for_type_in_cloned_ref(&mut err, place); let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); - if let Some(expr) = self.find_expr(borrow_span) - && let Some(ty) = typeck_results.node_type_opt(expr.hir_id) - { - self.suggest_cloning(&mut err, ty, expr, self.find_expr(span), Some(move_spans)); + if let Some(expr) = self.find_expr(borrow_span) { + // This is a borrow span, so we want to suggest cloning the referent. + if let hir::ExprKind::AddrOf(_, _, borrowed_expr) = expr.kind + && let Some(ty) = typeck_results.expr_ty_opt(borrowed_expr) + { + self.suggest_cloning(&mut err, ty, borrowed_expr, Some(move_spans)); + } else if typeck_results.expr_adjustments(expr).first().is_some_and(|adj| { + matches!( + adj.kind, + ty::adjustment::Adjust::Borrow(ty::adjustment::AutoBorrow::Ref( + _, + ty::adjustment::AutoBorrowMutability::Not + | ty::adjustment::AutoBorrowMutability::Mut { + allow_two_phase_borrow: ty::adjustment::AllowTwoPhase::No + } + )) + ) + }) && let Some(ty) = typeck_results.expr_ty_opt(expr) + { + self.suggest_cloning(&mut err, ty, expr, Some(move_spans)); + } } self.buffer_error(err); } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 636ae7b626809..792f1548e081e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -564,9 +564,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { match error { - GroupedMoveError::MovesFromPlace { - mut binds_to, move_from, span: other_span, .. - } => { + GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { self.add_borrow_suggestions(err, span); if binds_to.is_empty() { let place_ty = move_from.ty(self.body, self.infcx.tcx).ty; @@ -576,7 +574,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { }; if let Some(expr) = self.find_expr(span) { - self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span), None); + self.suggest_cloning(err, place_ty, expr, None); } err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { @@ -608,13 +606,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { }; if let Some(expr) = self.find_expr(use_span) { - self.suggest_cloning( - err, - place_ty, - expr, - self.find_expr(span), - Some(use_spans), - ); + self.suggest_cloning(err, place_ty, expr, Some(use_spans)); } err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { @@ -739,7 +731,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { let place_desc = &format!("`{}`", self.local_names[*local].unwrap()); if let Some(expr) = self.find_expr(binding_span) { - self.suggest_cloning(err, bind_to.ty, expr, None, None); + self.suggest_cloning(err, bind_to.ty, expr, None); } err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index bbb5daccfd639..b13773ffe1460 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2330,10 +2330,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match (cast_ty_from, cast_ty_to) { (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => { let mut normalize = |t| self.normalize(t, location); + + // N.B. `struct_tail_with_normalize` only "structurally resolves" + // the type. It is not fully normalized, so we have to normalize it + // afterwards. let src_tail = tcx.struct_tail_with_normalize(src.ty, &mut normalize, || ()); + let src_tail = normalize(src_tail); let dst_tail = tcx.struct_tail_with_normalize(dst.ty, &mut normalize, || ()); + let dst_tail = normalize(dst_tail); // This checks (lifetime part of) vtable validity for pointer casts, // which is irrelevant when there are aren't principal traits on both sides (aka only auto traits). diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index a30ab236213a0..9695df9c87e8a 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -115,9 +115,6 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values .suggestion = remove the value -builtin_macros_derive_unsafe_path = traits in `#[derive(...)]` don't accept `unsafe(...)` - .suggestion = remove the `unsafe(...)` - builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time @@ -199,6 +196,9 @@ builtin_macros_format_use_positional = consider using a positional formatting ar builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!` +builtin_macros_global_asm_unsupported_operand = the `{$symbol}` operand cannot be used with `global_asm!` + .label = the `{$symbol}` operand is not meaningful for global-scoped inline assembly, remove it + builtin_macros_global_asm_unsupported_option = the `{$symbol}` option cannot be used with `global_asm!` .label = the `{$symbol}` option is not meaningful for global-scoped inline assembly .suggestion = remove this option diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 09d892768b464..1df2812e0c887 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -80,7 +80,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let params = thin_vec![cx.param(span, size, ty_usize.clone()), cx.param(span, align, ty_usize)]; let decl = cx.fn_decl(params, never); let header = FnHeader { safety: Safety::Unsafe(span), ..FnHeader::default() }; - let sig = FnSig { decl, header, span: span }; + let sig = FnSig { decl, header, span }; let body = Some(cx.block_expr(call)); let kind = ItemKind::Fn(Box::new(Fn { diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 06a49dc72b6f7..17439dd3e3eda 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -28,6 +28,29 @@ pub struct AsmArgs { pub options_spans: Vec, } +/// Used for better error messages when operand types are used that are not +/// supported by the current macro (e.g. `in` or `out` for `global_asm!`) +/// +/// returns +/// +/// - `Ok(true)` if the current token matches the keyword, and was expected +/// - `Ok(false)` if the current token does not match the keyword +/// - `Err(_)` if the current token matches the keyword, but was not expected +fn eat_operand_keyword<'a>(p: &mut Parser<'a>, symbol: Symbol, expect: bool) -> PResult<'a, bool> { + if expect { + Ok(p.eat_keyword(symbol)) + } else { + let span = p.token.span; + if p.eat_keyword_noexpect(symbol) { + // in gets printed as `r#in` otherwise + let symbol = if symbol == kw::In { "in" } else { symbol.as_str() }; + Err(p.dcx().create_err(errors::GlobalAsmUnsupportedOperand { span, symbol })) + } else { + Ok(false) + } + } +} + fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -105,7 +128,7 @@ pub fn parse_asm_args<'a>( }; let mut explicit_reg = false; - let op = if !is_global_asm && p.eat_keyword(kw::In) { + let op = if eat_operand_keyword(p, kw::In, !is_global_asm)? { let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); @@ -113,15 +136,15 @@ pub fn parse_asm_args<'a>( } let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } - } else if !is_global_asm && p.eat_keyword(sym::out) { + } else if eat_operand_keyword(p, sym::out, !is_global_asm)? { let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if !is_global_asm && p.eat_keyword(sym::lateout) { + } else if eat_operand_keyword(p, sym::lateout, !is_global_asm)? { let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if !is_global_asm && p.eat_keyword(sym::inout) { + } else if eat_operand_keyword(p, sym::inout, !is_global_asm)? { let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); @@ -135,7 +158,7 @@ pub fn parse_asm_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: false } } - } else if !is_global_asm && p.eat_keyword(sym::inlateout) { + } else if eat_operand_keyword(p, sym::inlateout, !is_global_asm)? { let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span }); @@ -149,6 +172,9 @@ pub fn parse_asm_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: true } } + } else if eat_operand_keyword(p, sym::label, !is_global_asm)? { + let block = p.parse_block()?; + ast::InlineAsmOperand::Label { block } } else if p.eat_keyword(kw::Const) { let anon_const = p.parse_expr_anon_const()?; ast::InlineAsmOperand::Const { anon_const } @@ -164,9 +190,6 @@ pub fn parse_asm_args<'a>( path: path.clone(), }; ast::InlineAsmOperand::Sym { sym } - } else if !is_global_asm && p.eat_keyword(sym::label) { - let block = p.parse_block()?; - ast::InlineAsmOperand::Label { block } } else if allow_templates { let template = p.parse_expr()?; // If it can't possibly expand to a string, provide diagnostics here to include other diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index ceb62230ece96..de198115fa00b 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -6,6 +6,7 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; +use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_span::Span; use {rustc_ast as ast, rustc_attr as attr}; @@ -42,7 +43,7 @@ fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, return Err(cx.dcx().create_err(errors::RequiresCfgPattern { span })); } - let cfg = p.parse_meta_item()?; + let cfg = p.parse_meta_item(AllowLeadingUnsafe::Yes)?; let _ = p.eat(&token::Comma); diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index 2d4a93776bb66..006b6aa823fbf 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -47,11 +47,13 @@ impl MultiItemModifier for Expander { ) -> ExpandResult, Annotatable> { let template = AttributeTemplate { list: Some("path"), ..Default::default() }; validate_attr::check_builtin_meta_item( + &ecx.ecfg.features, &ecx.sess.psess, meta_item, ast::AttrStyle::Outer, sym::cfg_accessible, template, + true, ); let Some(path) = validate_input(ecx, meta_item) else { diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index dc1874bfecbda..4b05c144d37d7 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -202,7 +202,7 @@ impl CfgEval<'_> { } // Now that we have our re-parsed `AttrTokenStream`, recursively configuring - // our attribute target will correctly the tokens as well. + // our attribute target will correctly configure the tokens as well. flat_map_annotatable(self, annotatable) } } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 03970a48638a0..57bddf0ab60ad 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,5 +1,5 @@ use rustc_ast as ast; -use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind}; +use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; @@ -38,11 +38,13 @@ impl MultiItemModifier for Expander { let template = AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }; validate_attr::check_builtin_meta_item( + features, &sess.psess, meta_item, ast::AttrStyle::Outer, sym::derive, template, + true, ); let mut resolutions = match &meta_item.kind { @@ -60,7 +62,6 @@ impl MultiItemModifier for Expander { // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the // paths. report_path_args(sess, meta); - report_unsafe_args(sess, meta); meta.path.clone() }) .map(|path| DeriveResolution { @@ -160,13 +161,3 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) { } } } - -fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) { - match meta.unsafety { - Safety::Unsafe(span) => { - sess.dcx().emit_err(errors::DeriveUnsafePath { span }); - } - Safety::Default => {} - Safety::Safe(_) => unreachable!(), - } -} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index ba84323a4d00a..c8ab8ed681c15 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -181,11 +181,10 @@ use std::{iter, vec}; use rustc_ast::ptr::P; use rustc_ast::{ self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, - Mutability, PatKind, TyKind, VariantData, + Mutability, PatKind, VariantData, }; use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_session::lint::builtin::BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use thin_vec::{thin_vec, ThinVec}; @@ -1599,52 +1598,11 @@ impl<'a> TraitDef<'a> { ), ); if is_packed { - // In general, fields in packed structs are copied via a - // block, e.g. `&{self.0}`. The two exceptions are `[u8]` - // and `str` fields, which cannot be copied and also never - // cause unaligned references. These exceptions are allowed - // to handle the `FlexZeroSlice` type in the `zerovec` - // crate within `icu4x-0.9.0`. - // - // Once use of `icu4x-0.9.0` has dropped sufficiently, this - // exception should be removed. - let is_simple_path = |ty: &P, sym| { - if let TyKind::Path(None, ast::Path { segments, .. }) = &ty.kind - && let [seg] = segments.as_slice() - && seg.ident.name == sym - && seg.args.is_none() - { - true - } else { - false - } - }; - - let exception = if let TyKind::Slice(ty) = &struct_field.ty.kind - && is_simple_path(ty, sym::u8) - { - Some("byte") - } else if is_simple_path(&struct_field.ty, sym::str) { - Some("string") - } else { - None - }; - - if let Some(ty) = exception { - cx.sess.psess.buffer_lint( - BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, - sp, - ast::CRATE_NODE_ID, - rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive { - ty: ty.to_string(), - }, - ); - } else { - // Wrap the expression in `{...}`, causing a copy. - field_expr = cx.expr_block( - cx.block(struct_field.span, thin_vec![cx.stmt_expr(field_expr)]), - ); - } + // Fields in packed structs are wrapped in a block, e.g. `&{self.0}`, + // causing a copy instead of a (potentially misaligned) reference. + field_expr = cx.expr_block( + cx.block(struct_field.span, thin_vec![cx.stmt_expr(field_expr)]), + ); } cx.expr_addr_of(sp, field_expr) }) diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs index bbc7cd3962720..7eb1f17a59ce3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs +++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs @@ -1,14 +1,16 @@ -use std::mem::swap; - +use ast::ptr::P; use ast::HasAttrs; +use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::visit::BoundKind; use rustc_ast::{ self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, - TraitBoundModifiers, VariantData, + TraitBoundModifiers, VariantData, WherePredicate, }; use rustc_attr as attr; +use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use smallvec::{smallvec, SmallVec}; use thin_vec::{thin_vec, ThinVec}; @@ -141,33 +143,237 @@ pub fn expand_deriving_smart_ptr( alt_self_params[pointee_param_idx] = GenericArg::Type(s_ty.clone()); let alt_self_type = cx.ty_path(cx.path_all(span, false, vec![name_ident], alt_self_params)); + // # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location + // // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. let mut impl_generics = generics.clone(); + let pointee_ty_ident = generics.params[pointee_param_idx].ident; + let mut self_bounds; { - let p = &mut impl_generics.params[pointee_param_idx]; + let pointee = &mut impl_generics.params[pointee_param_idx]; + self_bounds = pointee.bounds.clone(); + if !contains_maybe_sized_bound(&self_bounds) + && !contains_maybe_sized_bound_on_pointee( + &generics.where_clause.predicates, + pointee_ty_ident.name, + ) + { + cx.dcx() + .struct_span_err( + pointee_ty_ident.span, + format!( + "`derive(SmartPointer)` requires {} to be marked `?Sized`", + pointee_ty_ident.name + ), + ) + .emit(); + return; + } let arg = GenericArg::Type(s_ty.clone()); let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]); - p.bounds.push(cx.trait_bound(unsize, false)); - let mut attrs = thin_vec![]; - swap(&mut p.attrs, &mut attrs); - p.attrs = attrs.into_iter().filter(|attr| !attr.has_name(sym::pointee)).collect(); + pointee.bounds.push(cx.trait_bound(unsize, false)); + // Drop `#[pointee]` attribute since it should not be recognized outside `derive(SmartPointer)` + pointee.attrs.retain(|attr| !attr.has_name(sym::pointee)); + } + + // # Rewrite generic parameter bounds + // For each bound `U: ..` in `struct`, make a new bound with `__S` in place of `#[pointee]` + // Example: + // ``` + // struct< + // U: Trait, + // #[pointee] T: Trait + ?Sized, + // V: Trait> ... + // ``` + // ... generates this `impl` generic parameters + // ``` + // impl< + // U: Trait + Trait<__S>, + // T: Trait + ?Sized + Unsize<__S>, // (**) + // __S: Trait<__S> + ?Sized, // (*) + // V: Trait + Trait<__S>> ... + // ``` + // The new bound marked with (*) has to be done separately. + // See next section + for (idx, (params, orig_params)) in + impl_generics.params.iter_mut().zip(&generics.params).enumerate() + { + // Default type parameters are rejected for `impl` block. + // We should drop them now. + match &mut params.kind { + ast::GenericParamKind::Const { default, .. } => *default = None, + ast::GenericParamKind::Type { default } => *default = None, + ast::GenericParamKind::Lifetime => {} + } + // We CANNOT rewrite `#[pointee]` type parameter bounds. + // This has been set in stone. (**) + // So we skip over it. + // Otherwise, we push extra bounds involving `__S`. + if idx != pointee_param_idx { + for bound in &orig_params.bounds { + let mut bound = bound.clone(); + let mut substitution = TypeSubstitution { + from_name: pointee_ty_ident.name, + to_ty: &s_ty, + rewritten: false, + }; + substitution.visit_param_bound(&mut bound, BoundKind::Bound); + if substitution.rewritten { + // We found use of `#[pointee]` somewhere, + // so we make a new bound using `__S` in place of `#[pointee]` + params.bounds.push(bound); + } + } + } + } + + // # Insert `__S` type parameter + // + // We now insert `__S` with the missing bounds marked with (*) above. + // We should also write the bounds from `#[pointee]` to `__S` as required by `Unsize<__S>`. + { + let mut substitution = + TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false }; + for bound in &mut self_bounds { + substitution.visit_param_bound(bound, BoundKind::Bound); + } + } + + // # Rewrite `where` clauses + // + // Move on to `where` clauses. + // Example: + // ``` + // struct MyPointer<#[pointee] T, ..> + // where + // U: Trait + Trait, + // Companion: Trait, + // T: Trait + ?Sized, + // { .. } + // ``` + // ... will have a impl prelude like so + // ``` + // impl<..> .. + // where + // U: Trait + Trait, + // U: Trait<__S>, + // Companion: Trait, + // Companion<__S>: Trait<__S>, + // T: Trait + ?Sized, + // __S: Trait<__S> + ?Sized, + // ``` + // + // We should also write a few new `where` bounds from `#[pointee] T` to `__S` + // as well as any bound that indirectly involves the `#[pointee] T` type. + for bound in &generics.where_clause.predicates { + if let ast::WherePredicate::BoundPredicate(bound) = bound { + let mut substitution = TypeSubstitution { + from_name: pointee_ty_ident.name, + to_ty: &s_ty, + rewritten: false, + }; + let mut predicate = ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + span: bound.span, + bound_generic_params: bound.bound_generic_params.clone(), + bounded_ty: bound.bounded_ty.clone(), + bounds: bound.bounds.clone(), + }); + substitution.visit_where_predicate(&mut predicate); + if substitution.rewritten { + impl_generics.where_clause.predicates.push(predicate); + } + } } - // Add the `__S: ?Sized` extra parameter to the impl block. - let sized = cx.path_global(span, path!(span, core::marker::Sized)); - let bound = GenericBound::Trait( - cx.poly_trait_ref(span, sized), - TraitBoundModifiers { - polarity: ast::BoundPolarity::Maybe(span), - constness: ast::BoundConstness::Never, - asyncness: ast::BoundAsyncness::Normal, - }, - ); - let extra_param = cx.typaram(span, Ident::new(sym::__S, span), vec![bound], None); - impl_generics.params.push(extra_param); + let extra_param = cx.typaram(span, Ident::new(sym::__S, span), self_bounds, None); + impl_generics.params.insert(pointee_param_idx + 1, extra_param); // Add the impl blocks for `DispatchFromDyn` and `CoerceUnsized`. let gen_args = vec![GenericArg::Type(alt_self_type.clone())]; add_impl_block(impl_generics.clone(), sym::DispatchFromDyn, gen_args.clone()); add_impl_block(impl_generics.clone(), sym::CoerceUnsized, gen_args.clone()); } + +fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool { + for bound in predicates { + if let ast::WherePredicate::BoundPredicate(bound) = bound + && bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee) + { + for bound in &bound.bounds { + if is_maybe_sized_bound(bound) { + return true; + } + } + } + } + false +} + +fn is_maybe_sized_bound(bound: &GenericBound) -> bool { + if let GenericBound::Trait( + trait_ref, + TraitBoundModifiers { polarity: ast::BoundPolarity::Maybe(_), .. }, + ) = bound + { + is_sized_marker(&trait_ref.trait_ref.path) + } else { + false + } +} + +fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool { + bounds.iter().any(is_maybe_sized_bound) +} + +fn path_segment_is_exact_match(path_segments: &[ast::PathSegment], syms: &[Symbol]) -> bool { + path_segments.iter().zip(syms).all(|(segment, &symbol)| segment.ident.name == symbol) +} + +fn is_sized_marker(path: &ast::Path) -> bool { + const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized]; + const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized]; + if path.segments.len() == 4 && path.is_global() { + path_segment_is_exact_match(&path.segments[1..], &CORE_UNSIZE) + || path_segment_is_exact_match(&path.segments[1..], &STD_UNSIZE) + } else if path.segments.len() == 3 { + path_segment_is_exact_match(&path.segments, &CORE_UNSIZE) + || path_segment_is_exact_match(&path.segments, &STD_UNSIZE) + } else { + *path == sym::Sized + } +} + +struct TypeSubstitution<'a> { + from_name: Symbol, + to_ty: &'a ast::Ty, + rewritten: bool, +} + +impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { + fn visit_ty(&mut self, ty: &mut P) { + if let Some(name) = ty.kind.is_simple_path() + && name == self.from_name + { + **ty = self.to_ty.clone(); + self.rewritten = true; + } else { + ast::mut_visit::walk_ty(self, ty); + } + } + + fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) { + match where_predicate { + rustc_ast::WherePredicate::BoundPredicate(bound) => { + bound + .bound_generic_params + .flat_map_in_place(|param| self.flat_map_generic_param(param)); + self.visit_ty(&mut bound.bounded_ty); + for bound in &mut bound.bounds { + self.visit_param_bound(bound, BoundKind::Bound) + } + } + rustc_ast::WherePredicate::RegionPredicate(_) + | rustc_ast::WherePredicate::EqPredicate(_) => {} + } + } +} diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 0a4f07709c7fd..6ca43441e0582 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -297,13 +297,6 @@ pub(crate) struct DerivePathArgsValue { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(builtin_macros_derive_unsafe_path)] -pub(crate) struct DeriveUnsafePath { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(builtin_macros_no_default_variant)] #[help] @@ -858,6 +851,15 @@ pub(crate) struct GlobalAsmUnsupportedOption { pub(crate) full_span: Span, } +#[derive(Diagnostic)] +#[diag(builtin_macros_global_asm_unsupported_operand)] +pub(crate) struct GlobalAsmUnsupportedOperand<'a> { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) symbol: &'a str, +} + #[derive(Diagnostic)] #[diag(builtin_macros_test_runner_invalid)] pub(crate) struct TestRunnerInvalid { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 828708e34953e..a9ba7334d930e 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,7 +5,6 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 87187125541b2..869d203f68eba 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -24,7 +24,7 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P let mut parser = cx.new_parser_from_tts(stream); let ty = parser.parse_ty()?; - parser.eat_keyword(sym::is); + parser.expect_keyword(sym::is)?; let pat = parser.parse_pat_no_top_alt(None, None)?; Ok((ty, pat)) diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 65b50736c5555..73cc8ff547d51 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -17,11 +17,13 @@ pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaI // All the built-in macro attributes are "words" at the moment. let template = AttributeTemplate { word: true, ..Default::default() }; validate_attr::check_builtin_meta_item( + &ecx.ecfg.features, &ecx.sess.psess, meta_item, AttrStyle::Outer, name, template, + true, ); } diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 9a28428cb62c3..2f5a864d70eaf 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -97,7 +97,9 @@ impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> { fn build(mut self: Box, output: &Path) -> bool { match self.build_with_llvm(output) { Ok(any_members) => any_members, - Err(e) => self.sess.dcx().emit_fatal(ArchiveBuildFailure { error: e }), + Err(error) => { + self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error }) + } } } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ea930421b5869..14540d41e7c62 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -775,10 +775,10 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> ptr); - ifn!("llvm.powi.f16", fn(t_f16, t_i32) -> t_f16); - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.powi.f128", fn(t_f128, t_i32) -> t_f128); + ifn!("llvm.powi.f16.i32", fn(t_f16, t_i32) -> t_f16); + ifn!("llvm.powi.f32.i32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64.i32", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.powi.f128.i32", fn(t_f128, t_i32) -> t_f128); ifn!("llvm.pow.f16", fn(t_f16, t_f16) -> t_f16); ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 040de1c7dd715..57d5f6fdf503f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -35,10 +35,10 @@ fn get_simple_intrinsic<'ll>( sym::sqrtf64 => "llvm.sqrt.f64", sym::sqrtf128 => "llvm.sqrt.f128", - sym::powif16 => "llvm.powi.f16", - sym::powif32 => "llvm.powi.f32", - sym::powif64 => "llvm.powi.f64", - sym::powif128 => "llvm.powi.f128", + sym::powif16 => "llvm.powi.f16.i32", + sym::powif32 => "llvm.powi.f32.i32", + sym::powif64 => "llvm.powi.f64.i32", + sym::powif128 => "llvm.powi.f128.i32", sym::sinf16 => "llvm.sin.f16", sym::sinf32 => "llvm.sin.f32", diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index dc21b92a95f76..af8a9be1ccbfd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -646,6 +646,22 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec, which didn't make + // it into a released version of LLVM yet. + // + // This doesn't use the "implicit target feature" system because it is only + // used for function attributes in other targets, which fixes this bug as + // well on the function attribute level. + if sess.target.families.contains(&"wasm".into()) { + if features.iter().any(|f| f == "+relaxed-simd") + && !features.iter().any(|f| f == "+simd128") + { + features.push("+simd128".into()); + } + } + if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { sess.dcx().emit_err(TargetFeatureDisableOrEnable { features: f, diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 000fe2e3ce0f5..57d789aef80ca 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -4,8 +4,7 @@ codegen_ssa_add_native_library = failed to add native library {$library_path}: { codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error} -codegen_ssa_archive_build_failure = - failed to build archive: {$error} +codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering @@ -198,7 +197,7 @@ codegen_ssa_read_file = failed to read file: {$message} codegen_ssa_repair_vs_build_tools = the Visual Studio build tools may need to be repaired using the Visual Studio installer -codegen_ssa_rlib_archive_build_failure = failed to build archive from rlib: {$error} +codegen_ssa_rlib_archive_build_failure = failed to build archive from rlib at `{$path}`: {$error} codegen_ssa_rlib_incompatible_dependency_formats = `{$ty1}` and `{$ty2}` do not have equivalent dependency formats (`{$list1}` vs `{$list2}`) diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index fd39ef2649bf5..4f93b7b23c6e6 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -207,7 +207,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let sess = self.sess; match self.build_inner(output) { Ok(any_members) => any_members, - Err(e) => sess.dcx().emit_fatal(ArchiveBuildFailure { error: e }), + Err(error) => { + sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error }) + } } } } @@ -218,11 +220,7 @@ impl<'a> ArArchiveBuilder<'a> { "gnu" => ArchiveKind::Gnu, "bsd" => ArchiveKind::Bsd, "darwin" => ArchiveKind::Darwin, - "coff" => { - // FIXME: ar_archive_writer doesn't support COFF archives yet. - // https://github.com/rust-lang/ar_archive_writer/issues/9 - ArchiveKind::Gnu - } + "coff" => ArchiveKind::Coff, "aix_big" => ArchiveKind::AixBig, kind => { self.sess.dcx().emit_fatal(UnknownArchiveKind { kind }); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 55662bfc2cf37..653895380bebc 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2065,17 +2065,61 @@ fn add_local_crate_metadata_objects( } /// Add sysroot and other globally set directories to the directory search list. -fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) { - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); +fn add_library_search_dirs( + cmd: &mut dyn Linker, + sess: &Session, + self_contained_components: LinkSelfContainedComponents, + apple_sdk_root: Option<&Path>, +) { + if !sess.opts.unstable_opts.link_native_libraries { + return; + } - // Special directory with libraries used only in self-contained linkage mode - if self_contained { - let lib_path = sess.target_filesearch(PathKind::All).get_self_contained_lib_path(); + // Library search paths explicitly supplied by user (`-L` on the command line). + for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() { + cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)); + } + for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() { + // Contrary to the `-L` docs only framework-specific paths are considered here. + if search_path.kind != PathKind::All { + cmd.framework_path(&search_path.dir); + } + } + + // The toolchain ships some native library components and self-contained linking was enabled. + // Add the self-contained library directory to search paths. + if self_contained_components.intersects( + LinkSelfContainedComponents::LIBC + | LinkSelfContainedComponents::UNWIND + | LinkSelfContainedComponents::MINGW, + ) { + let lib_path = sess.target_filesearch(PathKind::Native).get_self_contained_lib_path(); cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); } + + // Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot + // library directory instead of the self-contained directories. + // Sanitizer libraries have the same issue and are also linked by name on Apple targets. + // The targets here should be in sync with `copy_third_party_objects` in bootstrap. + // FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind + // and sanitizers to self-contained directory, and stop adding this search path. + if sess.target.vendor == "fortanix" + || sess.target.os == "linux" + || sess.target.os == "fuchsia" + || sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty() + { + let lib_path = sess.target_filesearch(PathKind::Native).get_lib_path(); + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + } + + // Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks + // we must have the support library stubs in the library search path (#121430). + if let Some(sdk_root) = apple_sdk_root + && sess.target.llvm_target.contains("macabi") + { + cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib")); + cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks")); + } } /// Add options making relocation sections in the produced ELF files read-only @@ -2367,7 +2411,7 @@ fn add_order_independent_options( // Take care of the flavors and CLI options requesting the `lld` linker. add_lld_args(cmd, sess, flavor, self_contained_components); - add_apple_sdk(cmd, sess, flavor); + let apple_sdk_root = add_apple_sdk(cmd, sess, flavor); add_link_script(cmd, sess, tmpdir, crate_type); @@ -2423,7 +2467,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, self_contained_components.are_any_components_enabled()); + add_library_search_dirs(cmd, sess, self_contained_components, apple_sdk_root.as_deref()); cmd.output_filename(out_filename); @@ -2637,19 +2681,6 @@ fn add_local_native_libraries( tmpdir: &Path, link_output_kind: LinkOutputKind, ) { - if sess.opts.unstable_opts.link_native_libraries { - // User-supplied library search paths (-L on the command line). These are the same paths - // used to find Rust crates, so some of them may have been added already by the previous - // crate linking code. This only allows them to be found at compile time so it is still - // entirely up to outside forces to make sure that library can be found at runtime. - for search_path in sess.target_filesearch(PathKind::All).search_paths() { - match search_path.kind { - PathKind::Framework => cmd.framework_path(&search_path.dir), - _ => cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)), - } - } - } - // All static and dynamic native library dependencies are linked to the local crate. let link_static = true; let link_dynamic = true; @@ -2911,7 +2942,8 @@ fn add_static_crate( false }), ) { - sess.dcx().emit_fatal(errors::RlibArchiveBuildFailure { error }); + sess.dcx() + .emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error }); } if archive.build(&dst) { link_upstream(&dst); @@ -2943,7 +2975,7 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool } } -fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { +fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option { let arch = &sess.target.arch; let os = &sess.target.os; let llvm_target = &sess.target.llvm_target; @@ -2951,11 +2983,11 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos") || !matches!(flavor, LinkerFlavor::Darwin(..)) { - return; + return None; } if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) { - return; + return None; } let sdk_name = match (arch.as_ref(), os.as_ref()) { @@ -2979,14 +3011,14 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { (_, "macos") => "macosx", _ => { sess.dcx().emit_err(errors::UnsupportedArch { arch, os }); - return; + return None; } }; let sdk_root = match get_apple_sdk_root(sdk_name) { Ok(s) => s, Err(e) => { sess.dcx().emit_err(e); - return; + return None; } }; @@ -3006,16 +3038,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { _ => unreachable!(), } - if llvm_target.contains("macabi") { - // Mac Catalyst uses the macOS SDK, but to link to iOS-specific - // frameworks, we must have the support library stubs in the library - // search path. - - // The flags are called `-L` and `-F` both in Clang, ld64 and ldd. - let sdk_root = Path::new(&sdk_root); - cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib")); - cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks")); - } + Some(sdk_root.into()) } fn get_apple_sdk_root(sdk_name: &str) -> Result> { diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 741c0f090e98d..a972c0cd99d10 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use crate::traits::*; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum IntPredicate { IntEQ, IntNE, @@ -22,7 +22,7 @@ pub enum IntPredicate { IntSLE, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum RealPredicate { RealPredicateFalse, RealOEQ, @@ -42,7 +42,7 @@ pub enum RealPredicate { RealPredicateTrue, } -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum AtomicRmwBinOp { AtomicXchg, AtomicAdd, @@ -57,7 +57,7 @@ pub enum AtomicRmwBinOp { AtomicUMin, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum AtomicOrdering { Unordered, Relaxed, @@ -67,7 +67,7 @@ pub enum AtomicOrdering { SequentiallyConsistent, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum SynchronizationScope { SingleThread, CrossThread, diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 46d7cfe87e6ac..2d1eae630cf16 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -500,6 +500,7 @@ pub struct UnableToWriteDebuggerVisualizer { #[derive(Diagnostic)] #[diag(codegen_ssa_rlib_archive_build_failure)] pub struct RlibArchiveBuildFailure { + pub path: PathBuf, pub error: Error, } @@ -557,6 +558,7 @@ pub struct UnsupportedLinkSelfContained; #[diag(codegen_ssa_archive_build_failure)] // Public for rustc_codegen_llvm::back::archive pub struct ArchiveBuildFailure { + pub path: PathBuf, pub error: std::io::Error, } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 77da4e4caea0f..127244a34f8f0 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -78,6 +78,7 @@ pub fn from_target_feature( Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature, Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature, Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature, + Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86, Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics, Some(sym::xop_target_feature) => rust_features.xop_target_feature, Some(sym::s390x_target_feature) => rust_features.s390x_target_feature, @@ -96,6 +97,14 @@ pub fn from_target_feature( Some(Symbol::intern(feature)) })); } + + for (feature, requires) in tcx.sess.target.implicit_target_features() { + if target_features.iter().any(|f| f.as_str() == *feature) + && !target_features.iter().any(|f| f.as_str() == *requires) + { + target_features.push(Symbol::intern(requires)); + } + } } /// Computes the set of target features used in a function for the purposes of diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 0495902dda511..2b802240e03b9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -23,7 +23,7 @@ use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::{PlaceRef, PlaceValue}; use crate::MemFlags; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum OverflowOp { Add, Sub, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 43f405b223504..c64c73b232340 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -88,10 +88,18 @@ const_eval_exact_div_has_remainder = exact_div: {$a} cannot be divided by {$b} without remainder const_eval_expected_inbounds_pointer = - expected {$inbounds_size -> - [0] a pointer to some allocation - [1] a pointer to 1 byte of memory - *[x] a pointer to {$inbounds_size} bytes of memory + expected a pointer to {$inbounds_size_abs -> + [0] some allocation + *[x] {$inbounds_size_is_neg -> + [false] {$inbounds_size_abs -> + [1] 1 byte of memory + *[x] {$inbounds_size_abs} bytes of memory + } + *[true] the end of {$inbounds_size_abs -> + [1] 1 byte of memory + *[x] {$inbounds_size_abs} bytes of memory + } + } } const_eval_extern_static = @@ -243,7 +251,7 @@ const_eval_offset_from_different_allocations = const_eval_offset_from_overflow = `{$name}` called when first pointer is too far ahead of second const_eval_offset_from_test = - out-of-bounds `offset_from` + out-of-bounds `offset_from` origin const_eval_offset_from_underflow = `{$name}` called when first pointer is too far before second const_eval_offset_from_unsigned_overflow = @@ -269,17 +277,24 @@ const_eval_partial_pointer_copy = const_eval_partial_pointer_overwrite = unable to overwrite parts of a pointer in memory at {$ptr} const_eval_pointer_arithmetic_overflow = - overflowing in-bounds pointer arithmetic + overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic const_eval_pointer_out_of_bounds = {$bad_pointer_message}: {const_eval_expected_inbounds_pointer}, but got {$pointer} {$ptr_offset_is_neg -> [true] which points to before the beginning of the allocation - *[false] {$alloc_size_minus_ptr_offset -> - [0] which is at or beyond the end of the allocation of size {$alloc_size -> - [1] 1 byte - *[x] {$alloc_size} bytes + *[false] {$inbounds_size_is_neg -> + [true] {$ptr_offset_abs -> + [0] which is at the beginning of the allocation + *[other] which does not have enough space to the beginning of the allocation + } + *[false] {$alloc_size_minus_ptr_offset -> + [0] which is at or beyond the end of the allocation of size {$alloc_size -> + [1] 1 byte + *[x] {$alloc_size} bytes + } + [1] which is only 1 byte from the end of the allocation + *[x] which is only {$alloc_size_minus_ptr_offset} bytes from the end of the allocation } - *[x] and there are only {$alloc_size_minus_ptr_offset} bytes starting at that pointer } } const_eval_pointer_use_after_free = @@ -301,9 +316,6 @@ const_eval_range_upper = less or equal to {$hi} const_eval_range_wrapping = less or equal to {$hi}, or greater or equal to {$lo} const_eval_raw_bytes = the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"} -const_eval_raw_eq_with_provenance = - `raw_eq` on bytes with provenance - const_eval_raw_ptr_comparison = pointers cannot be reliably compared during const eval .note = see issue #53020 for more information diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ffdf790da7ae0..3ded81b90ffc1 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -634,10 +634,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { trace!( "visit_projection_elem: place_ref={:?} elem={:?} \ context={:?} location={:?}", - place_ref, - elem, - context, - location, + place_ref, elem, context, location, ); self.super_projection_elem(place_ref, elem, context, location); diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index ba4b80d102697..ff27e4000163a 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -73,7 +73,9 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( cid.promoted.map_or_else(String::new, |p| format!("::{p:?}")) ); - ecx.push_stack_frame( + // This can't use `init_stack_frame` since `body` is not a function, + // so computing its ABI would fail. It's also not worth it since there are no arguments to pass. + ecx.push_stack_frame_raw( cid.instance, body, &ret.clone().into(), @@ -396,7 +398,7 @@ fn const_validate_mplace<'tcx>( let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let mut ref_tracking = RefTracking::new(mplace.clone()); let mut inner = false; - while let Some((mplace, path)) = ref_tracking.todo.pop() { + while let Some((mplace, path)) = ref_tracking.next() { let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) { _ if cid.promoted.is_some() => CtfeValidationMode::Promoted, Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static` diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 65cbeab24ec00..a075bdc191181 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -24,8 +24,9 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup, - throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame, + throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, + StackPopCleanup, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -295,7 +296,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { ); } - match self.ptr_try_get_alloc_id(ptr) { + match self.ptr_try_get_alloc_id(ptr, 0) { Ok((alloc_id, offset, _extra)) => { let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id); @@ -306,17 +307,15 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { let align = ImmTy::from_uint(target_align, args[1].layout).into(); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - // We replace the entire function call with a "tail call". - // Note that this happens before the frame of the original function - // is pushed on the stack. - self.eval_fn_call( - FnVal::Instance(instance), - (CallAbi::Rust, fn_abi), + // Push the stack frame with our own adjusted arguments. + self.init_stack_frame( + instance, + self.load_mir(instance.def, None)?, + fn_abi, &[FnArg::Copy(addr), FnArg::Copy(align)], /* with_caller_location = */ false, dest, - ret, - mir::UnwindAction::Unreachable, + StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable }, )?; Ok(ControlFlow::Break(())) } else { @@ -458,7 +457,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { _unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { // Shared intrinsics. - if ecx.emulate_intrinsic(instance, args, dest, target)? { + if ecx.eval_intrinsic(instance, args, dest, target)? { return Ok(None); } let intrinsic_name = ecx.tcx.item_name(instance.def_id()); @@ -510,7 +509,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // If an allocation is created in an another const, // we don't deallocate it. - let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?; + let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0)?; let is_allocated_in_another_const = matches!( ecx.tcx.try_get_global_alloc(alloc_id), Some(interpret::GlobalAlloc::Memory(_)) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 2dd8640009a69..7afb92c08ec9a 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::fmt::Write; use either::Either; use rustc_errors::codes::*; @@ -15,7 +16,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::Span; use rustc_target::abi::call::AdjustForForeignAbiError; -use rustc_target::abi::{Size, WrappingRange}; +use rustc_target::abi::WrappingRange; use crate::interpret::InternKind; @@ -575,18 +576,21 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { .arg("bad_pointer_message", bad_pointer_message(msg, dcx)); } PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, inbounds_size, msg } => { - diag.arg("alloc_size", alloc_size.bytes()) - .arg("inbounds_size", inbounds_size.bytes()) - .arg("bad_pointer_message", bad_pointer_message(msg, dcx)); - diag.arg( - "pointer", - Pointer::new( - Some(CtfeProvenance::from(alloc_id)), - Size::from_bytes(ptr_offset as u64), - ) - .to_string(), - ); + diag.arg("alloc_size", alloc_size.bytes()); + diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx)); + diag.arg("pointer", { + let mut out = format!("{:?}", alloc_id); + if ptr_offset > 0 { + write!(out, "+{:#x}", ptr_offset).unwrap(); + } else if ptr_offset < 0 { + write!(out, "-{:#x}", ptr_offset.unsigned_abs()).unwrap(); + } + out + }); + diag.arg("inbounds_size_is_neg", inbounds_size < 0); + diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); diag.arg("ptr_offset_is_neg", ptr_offset < 0); + diag.arg("ptr_offset_abs", ptr_offset.unsigned_abs()); diag.arg( "alloc_size_minus_ptr_offset", alloc_size.bytes().saturating_sub(ptr_offset as u64), @@ -600,7 +604,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { ); } - diag.arg("inbounds_size", inbounds_size.bytes()); + diag.arg("inbounds_size_is_neg", inbounds_size < 0); + diag.arg("inbounds_size_abs", inbounds_size.unsigned_abs()); diag.arg("bad_pointer_message", bad_pointer_message(msg, dcx)); } AlignmentCheckFailed(Misalignment { required, has }, msg) => { diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/call.rs similarity index 59% rename from compiler/rustc_const_eval/src/interpret/terminator.rs rename to compiler/rustc_const_eval/src/interpret/call.rs index 5de42ab960852..2c5147678e8cb 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -1,24 +1,23 @@ +//! Manages calling a concrete function (with known MIR body) with argument passing, +//! and returning the return value to the caller. use std::borrow::Cow; -use either::Either; +use either::{Left, Right}; use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; -use rustc_span::source_map::Spanned; use rustc_span::sym; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; use rustc_target::abi::{self, FieldIdx, Integer}; use rustc_target::spec::abi::Abi; -use tracing::trace; +use tracing::{info, instrument, trace}; use super::{ throw_ub, throw_ub_custom, throw_unsup_format, CtfeProvenance, FnVal, ImmTy, InterpCx, - InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, Scalar, - StackPopCleanup, + InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, ReturnAction, Scalar, + StackPopCleanup, StackPopInfo, }; use crate::fluent_generated as fluent; -use crate::interpret::eval_context::StackPopInfo; -use crate::interpret::ReturnAction; /// An argment passed to a function. #[derive(Clone, Debug)] @@ -39,15 +38,6 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> { } } -struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> { - callee: FnVal<'tcx, M::ExtraFnVal>, - args: Vec>, - fn_sig: ty::FnSig<'tcx>, - fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, - /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`]) - with_caller_location: bool, -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. @@ -67,7 +57,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { args.iter().map(|fn_arg| self.copy_fn_arg(fn_arg)).collect() } - pub fn fn_arg_field( + /// Helper function for argument untupling. + pub(super) fn fn_arg_field( &self, arg: &FnArg<'tcx, M::Provenance>, field: usize, @@ -78,190 +69,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }) } - pub(super) fn eval_terminator( - &mut self, - terminator: &mir::Terminator<'tcx>, - ) -> InterpResult<'tcx> { - use rustc_middle::mir::TerminatorKind::*; - match terminator.kind { - Return => { - self.return_from_current_stack_frame(/* unwinding */ false)? - } - - Goto { target } => self.go_to_block(target), - - SwitchInt { ref discr, ref targets } => { - let discr = self.read_immediate(&self.eval_operand(discr, None)?)?; - trace!("SwitchInt({:?})", *discr); - - // Branch to the `otherwise` case by default, if no match is found. - let mut target_block = targets.otherwise(); - - for (const_int, target) in targets.iter() { - // Compare using MIR BinOp::Eq, to also support pointer values. - // (Avoiding `self.binary_op` as that does some redundant layout computation.) - let res = self.binary_op( - mir::BinOp::Eq, - &discr, - &ImmTy::from_uint(const_int, discr.layout), - )?; - if res.to_scalar().to_bool()? { - target_block = target; - break; - } - } - - self.go_to_block(target_block); - } - - Call { - ref func, - ref args, - destination, - target, - unwind, - call_source: _, - fn_span: _, - } => { - let old_stack = self.frame_idx(); - let old_loc = self.frame().loc; - - let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; - - let destination = self.force_allocation(&self.eval_place(destination)?)?; - self.eval_fn_call( - callee, - (fn_sig.abi, fn_abi), - &args, - with_caller_location, - &destination, - target, - if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable }, - )?; - // Sanity-check that `eval_fn_call` either pushed a new frame or - // did a jump to another block. - if self.frame_idx() == old_stack && self.frame().loc == old_loc { - span_bug!(terminator.source_info.span, "evaluating this call made no progress"); - } - } - - TailCall { ref func, ref args, fn_span: _ } => { - let old_frame_idx = self.frame_idx(); - - let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = - self.eval_callee_and_args(terminator, func, args)?; - - self.eval_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; - - if self.frame_idx() != old_frame_idx { - span_bug!( - terminator.source_info.span, - "evaluating this tail call pushed a new stack frame" - ); - } - } - - Drop { place, target, unwind, replace: _ } => { - let place = self.eval_place(place)?; - let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); - if let ty::InstanceKind::DropGlue(_, None) = instance.def { - // This is the branch we enter if and only if the dropped type has no drop glue - // whatsoever. This can happen as a result of monomorphizing a drop of a - // generic. In order to make sure that generic and non-generic code behaves - // roughly the same (and in keeping with Mir semantics) we do nothing here. - self.go_to_block(target); - return Ok(()); - } - trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty); - self.drop_in_place(&place, instance, target, unwind)?; - } - - Assert { ref cond, expected, ref msg, target, unwind } => { - let ignored = - M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check(); - let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?; - if ignored || expected == cond_val { - self.go_to_block(target); - } else { - M::assert_panic(self, msg, unwind)?; - } - } - - UnwindTerminate(reason) => { - M::unwind_terminate(self, reason)?; - } - - // When we encounter Resume, we've finished unwinding - // cleanup for the current stack frame. We pop it in order - // to continue unwinding the next frame - UnwindResume => { - trace!("unwinding: resuming from cleanup"); - // By definition, a Resume terminator means - // that we're unwinding - self.return_from_current_stack_frame(/* unwinding */ true)?; - return Ok(()); - } - - // It is UB to ever encounter this. - Unreachable => throw_ub!(Unreachable), - - // These should never occur for MIR we actually run. - FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!( - terminator.source_info.span, - "{:#?} should have been eliminated by MIR pass", - terminator.kind - ), - - InlineAsm { template, ref operands, options, ref targets, .. } => { - M::eval_inline_asm(self, template, operands, options, targets)?; - } - } - - Ok(()) - } - - /// Evaluate the arguments of a function call - pub(super) fn eval_fn_call_arguments( - &self, - ops: &[Spanned>], - ) -> InterpResult<'tcx, Vec>> { - ops.iter() - .map(|op| { - let arg = match &op.node { - mir::Operand::Copy(_) | mir::Operand::Constant(_) => { - // Make a regular copy. - let op = self.eval_operand(&op.node, None)?; - FnArg::Copy(op) - } - mir::Operand::Move(place) => { - // If this place lives in memory, preserve its location. - // We call `place_to_op` which will be an `MPlaceTy` whenever there exists - // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local` - // which can return a local even if that has an mplace.) - let place = self.eval_place(*place)?; - let op = self.place_to_op(&place)?; - - match op.as_mplace_or_imm() { - Either::Left(mplace) => FnArg::InPlace(mplace), - Either::Right(_imm) => { - // This argument doesn't live in memory, so there's no place - // to make inaccessible during the call. - // We rely on there not being any stray `PlaceTy` that would let the - // caller directly access this local! - // This is also crucial for tail calls, where we want the `FnArg` to - // stay valid when the old stack frame gets popped. - FnArg::Copy(op) - } - } - } - }; - - Ok(arg) - }) - .collect() - } - /// Find the wrapped inner type of a transparent wrapper. /// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field"). /// @@ -431,8 +238,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else { trace!( "check_argument_compat: incompatible ABIs:\ncaller: {:?}\ncallee: {:?}", - caller_abi, - callee_abi + caller_abi, callee_abi ); return Ok(false); } @@ -504,46 +310,205 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } - /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the - /// necessary information about callee and arguments to make a call. - fn eval_callee_and_args( - &self, - terminator: &mir::Terminator<'tcx>, - func: &mir::Operand<'tcx>, - args: &[Spanned>], - ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { - let func = self.eval_operand(func, None)?; - let args = self.eval_fn_call_arguments(args)?; - - let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); - let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); - let extra_args = &args[fn_sig.inputs().len()..]; - let extra_args = - self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); - - let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { - ty::FnPtr(_sig) => { - let fn_ptr = self.read_pointer(&func)?; - let fn_val = self.get_ptr_fn(fn_ptr)?; - (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) - } - ty::FnDef(def_id, args) => { - let instance = self.resolve(def_id, args)?; - ( - FnVal::Instance(instance), - self.fn_abi_of_instance(instance, extra_args)?, - instance.def.requires_caller_location(*self.tcx), + fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { + // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 + let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + if !self.tcx.sess.target.is_like_wasm + && attrs + .target_features + .iter() + .any(|feature| !self.tcx.sess.target_features.contains(feature)) + { + throw_ub_custom!( + fluent::const_eval_unavailable_target_features_for_fn, + unavailable_feats = attrs + .target_features + .iter() + .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) + .fold(String::new(), |mut s, feature| { + if !s.is_empty() { + s.push_str(", "); + } + s.push_str(feature.as_str()); + s + }), + ); + } + Ok(()) + } + + /// The main entry point for creating a new stack frame: performs ABI checks and initializes + /// arguments. + #[instrument(skip(self), level = "trace")] + pub fn init_stack_frame( + &mut self, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[FnArg<'tcx, M::Provenance>], + with_caller_location: bool, + destination: &MPlaceTy<'tcx, M::Provenance>, + mut stack_pop: StackPopCleanup, + ) -> InterpResult<'tcx> { + // Compute callee information. + // FIXME: for variadic support, do we have to somehow determine callee's extra_args? + let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; + + if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { + throw_unsup_format!("calling a c-variadic function is not supported"); + } + + if M::enforce_abi(self) { + if caller_fn_abi.conv != callee_fn_abi.conv { + throw_ub_custom!( + fluent::const_eval_incompatible_calling_conventions, + callee_conv = format!("{:?}", callee_fn_abi.conv), + caller_conv = format!("{:?}", caller_fn_abi.conv), ) } - _ => { - span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty) + } + + // Check that all target features required by the callee (i.e., from + // the attribute `#[target_feature(enable = ...)]`) are enabled at + // compile time. + self.check_fn_target_features(instance)?; + + if !callee_fn_abi.can_unwind { + // The callee cannot unwind, so force the `Unreachable` unwind handling. + match &mut stack_pop { + StackPopCleanup::Root { .. } => {} + StackPopCleanup::Goto { unwind, .. } => { + *unwind = mir::UnwindAction::Unreachable; + } + } + } + + self.push_stack_frame_raw(instance, body, destination, stack_pop)?; + + // If an error is raised here, pop the frame again to get an accurate backtrace. + // To this end, we wrap it all in a `try` block. + let res: InterpResult<'tcx> = try { + trace!( + "caller ABI: {:#?}, args: {:#?}", + caller_fn_abi, + args.iter() + .map(|arg| ( + arg.layout().ty, + match arg { + FnArg::Copy(op) => format!("copy({op:?})"), + FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), + } + )) + .collect::>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + body.spread_arg, + body.args_iter() + .map(|local| ( + local, + self.layout_of_local(self.frame(), local, None).unwrap().ty, + )) + .collect::>() + ); + + // In principle, we have two iterators: Where the arguments come from, and where + // they go to. + + // The "where they come from" part is easy, we expect the caller to do any special handling + // that might be required here (e.g. for untupling). + // If `with_caller_location` is set we pretend there is an extra argument (that + // we will not pass; our `caller_location` intrinsic implementation walks the stack instead). + assert_eq!( + args.len() + if with_caller_location { 1 } else { 0 }, + caller_fn_abi.args.len(), + "mismatch between caller ABI and caller arguments", + ); + let mut caller_args = args + .iter() + .zip(caller_fn_abi.args.iter()) + .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); + + // Now we have to spread them out across the callee's locals, + // taking into account the `spread_arg`. If we could write + // this is a single iterator (that handles `spread_arg`), then + // `pass_argument` would be the loop body. It takes care to + // not advance `caller_iter` for ignored arguments. + let mut callee_args_abis = callee_fn_abi.args.iter(); + for local in body.args_iter() { + // Construct the destination place for this argument. At this point all + // locals are still dead, so we cannot construct a `PlaceTy`. + let dest = mir::Place::from(local); + // `layout_of_local` does more than just the instantiation we need to get the + // type, but the result gets cached so this avoids calling the instantiation + // query *again* the next time this local is accessed. + let ty = self.layout_of_local(self.frame(), local, None)?.ty; + if Some(local) == body.spread_arg { + // Make the local live once, then fill in the value field by field. + self.storage_live(local)?; + // Must be a tuple + let ty::Tuple(fields) = ty.kind() else { + span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}") + }; + for (i, field_ty) in fields.iter().enumerate() { + let dest = dest.project_deeper( + &[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)], + *self.tcx, + ); + let callee_abi = callee_args_abis.next().unwrap(); + self.pass_argument( + &mut caller_args, + callee_abi, + &dest, + field_ty, + /* already_live */ true, + )?; + } + } else { + // Normal argument. Cannot mark it as live yet, it might be unsized! + let callee_abi = callee_args_abis.next().unwrap(); + self.pass_argument( + &mut caller_args, + callee_abi, + &dest, + ty, + /* already_live */ false, + )?; + } + } + // If the callee needs a caller location, pretend we consume one more argument from the ABI. + if instance.def.requires_caller_location(*self.tcx) { + callee_args_abis.next().unwrap(); + } + // Now we should have no more caller args or callee arg ABIs + assert!( + callee_args_abis.next().is_none(), + "mismatch between callee ABI and callee body arguments" + ); + if caller_args.next().is_some() { + throw_ub_custom!(fluent::const_eval_too_many_caller_args); + } + // Don't forget to check the return type! + if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { + throw_ub!(AbiMismatchReturn { + caller_ty: caller_fn_abi.ret.layout.ty, + callee_ty: callee_fn_abi.ret.layout.ty + }); } - }; - Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + // Protect return place for in-place return value passing. + M::protect_in_place_function_argument(self, &destination)?; + + // Don't forget to mark "initially live" locals as live. + self.storage_live_for_always_live_locals()?; + }; + res.inspect_err(|_| { + // Don't show the incomplete stack frame in the error stacktrace. + self.stack_mut().pop(); + }) } - /// Call this function -- pushing the stack frame and initializing the arguments. + /// Initiate a call to this function -- pushing the stack frame and initializing the arguments. /// /// `caller_fn_abi` is used to determine if all the arguments are passed the proper way. /// However, we also need `caller_abi` to determine if we need to do untupling of arguments. @@ -551,7 +516,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// `with_caller_location` indicates whether the caller passed a caller location. Miri /// implements caller locations without argument passing, but to match `FnAbi` we need to know /// when those arguments are present. - pub(crate) fn eval_fn_call( + pub(super) fn init_fn_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), @@ -559,9 +524,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { with_caller_location: bool, destination: &MPlaceTy<'tcx, M::Provenance>, target: Option, - mut unwind: mir::UnwindAction, + unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - trace!("eval_fn_call: {:#?}", fn_val); + trace!("init_fn_call: {:#?}", fn_val); let instance = match fn_val { FnVal::Instance(instance) => instance, @@ -592,7 +557,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )? { assert!(!self.tcx.intrinsic(fallback.def_id()).unwrap().must_be_overridden); assert!(matches!(fallback.def, ty::InstanceKind::Item(_))); - return self.eval_fn_call( + return self.init_fn_call( FnVal::Instance(fallback), (caller_abi, caller_fn_abi), args, @@ -631,189 +596,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return Ok(()); }; - // Compute callee information using the `instance` returned by - // `find_mir_or_eval_fn`. - // FIXME: for variadic support, do we have to somehow determine callee's extra_args? - let callee_fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - - if callee_fn_abi.c_variadic || caller_fn_abi.c_variadic { - throw_unsup_format!("calling a c-variadic function is not supported"); - } - - if M::enforce_abi(self) { - if caller_fn_abi.conv != callee_fn_abi.conv { - throw_ub_custom!( - fluent::const_eval_incompatible_calling_conventions, - callee_conv = format!("{:?}", callee_fn_abi.conv), - caller_conv = format!("{:?}", caller_fn_abi.conv), + // Special handling for the closure ABI: untuple the last argument. + let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = + if caller_abi == Abi::RustCall && !args.is_empty() { + // Untuple + let (untuple_arg, args) = args.split_last().unwrap(); + trace!("init_fn_call: Will pass last argument by untupling"); + Cow::from( + args.iter() + .map(|a| Ok(a.clone())) + .chain( + (0..untuple_arg.layout().fields.count()) + .map(|i| self.fn_arg_field(untuple_arg, i)), + ) + .collect::>>()?, ) - } - } - - // Check that all target features required by the callee (i.e., from - // the attribute `#[target_feature(enable = ...)]`) are enabled at - // compile time. - self.check_fn_target_features(instance)?; - - if !callee_fn_abi.can_unwind { - // The callee cannot unwind, so force the `Unreachable` unwind handling. - unwind = mir::UnwindAction::Unreachable; - } + } else { + // Plain arg passing + Cow::from(args) + }; - self.push_stack_frame( + self.init_stack_frame( instance, body, + caller_fn_abi, + &args, + with_caller_location, destination, StackPopCleanup::Goto { ret: target, unwind }, - )?; - - // If an error is raised here, pop the frame again to get an accurate backtrace. - // To this end, we wrap it all in a `try` block. - let res: InterpResult<'tcx> = try { - trace!( - "caller ABI: {:?}, args: {:#?}", - caller_abi, - args.iter() - .map(|arg| ( - arg.layout().ty, - match arg { - FnArg::Copy(op) => format!("copy({op:?})"), - FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), - } - )) - .collect::>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - body.spread_arg, - body.args_iter() - .map(|local| ( - local, - self.layout_of_local(self.frame(), local, None).unwrap().ty, - )) - .collect::>() - ); - - // In principle, we have two iterators: Where the arguments come from, and where - // they go to. - - // For where they come from: If the ABI is RustCall, we untuple the - // last incoming argument. These two iterators do not have the same type, - // so to keep the code paths uniform we accept an allocation - // (for RustCall ABI only). - let caller_args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = - if caller_abi == Abi::RustCall && !args.is_empty() { - // Untuple - let (untuple_arg, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - Cow::from( - args.iter() - .map(|a| Ok(a.clone())) - .chain( - (0..untuple_arg.layout().fields.count()) - .map(|i| self.fn_arg_field(untuple_arg, i)), - ) - .collect::>>()?, - ) - } else { - // Plain arg passing - Cow::from(args) - }; - // If `with_caller_location` is set we pretend there is an extra argument (that - // we will not pass). - assert_eq!( - caller_args.len() + if with_caller_location { 1 } else { 0 }, - caller_fn_abi.args.len(), - "mismatch between caller ABI and caller arguments", - ); - let mut caller_args = caller_args - .iter() - .zip(caller_fn_abi.args.iter()) - .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); - - // Now we have to spread them out across the callee's locals, - // taking into account the `spread_arg`. If we could write - // this is a single iterator (that handles `spread_arg`), then - // `pass_argument` would be the loop body. It takes care to - // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = callee_fn_abi.args.iter(); - for local in body.args_iter() { - // Construct the destination place for this argument. At this point all - // locals are still dead, so we cannot construct a `PlaceTy`. - let dest = mir::Place::from(local); - // `layout_of_local` does more than just the instantiation we need to get the - // type, but the result gets cached so this avoids calling the instantiation - // query *again* the next time this local is accessed. - let ty = self.layout_of_local(self.frame(), local, None)?.ty; - if Some(local) == body.spread_arg { - // Make the local live once, then fill in the value field by field. - self.storage_live(local)?; - // Must be a tuple - let ty::Tuple(fields) = ty.kind() else { - span_bug!(self.cur_span(), "non-tuple type for `spread_arg`: {ty}") - }; - for (i, field_ty) in fields.iter().enumerate() { - let dest = dest.project_deeper( - &[mir::ProjectionElem::Field( - FieldIdx::from_usize(i), - field_ty, - )], - *self.tcx, - ); - let callee_abi = callee_args_abis.next().unwrap(); - self.pass_argument( - &mut caller_args, - callee_abi, - &dest, - field_ty, - /* already_live */ true, - )?; - } - } else { - // Normal argument. Cannot mark it as live yet, it might be unsized! - let callee_abi = callee_args_abis.next().unwrap(); - self.pass_argument( - &mut caller_args, - callee_abi, - &dest, - ty, - /* already_live */ false, - )?; - } - } - // If the callee needs a caller location, pretend we consume one more argument from the ABI. - if instance.def.requires_caller_location(*self.tcx) { - callee_args_abis.next().unwrap(); - } - // Now we should have no more caller args or callee arg ABIs - assert!( - callee_args_abis.next().is_none(), - "mismatch between callee ABI and callee body arguments" - ); - if caller_args.next().is_some() { - throw_ub_custom!(fluent::const_eval_too_many_caller_args); - } - // Don't forget to check the return type! - if !self.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { - throw_ub!(AbiMismatchReturn { - caller_ty: caller_fn_abi.ret.layout.ty, - callee_ty: callee_fn_abi.ret.layout.ty - }); - } - - // Protect return place for in-place return value passing. - M::protect_in_place_function_argument(self, &destination)?; - - // Don't forget to mark "initially live" locals as live. - self.storage_live_for_always_live_locals()?; - }; - match res { - Err(err) => { - self.stack_mut().pop(); - Err(err) - } - Ok(()) => Ok(()), - } + ) } // `InstanceKind::Virtual` does not have callable MIR. Calls to `Virtual` instances must be // codegen'd / interpreted as virtual calls through the vtable. @@ -936,7 +747,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { caller_fn_abi.args[0].layout.ty = receiver_ty; // recurse with concrete function - self.eval_fn_call( + self.init_fn_call( FnVal::Instance(fn_inst), (caller_abi, &caller_fn_abi), &args, @@ -949,30 +760,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - pub(crate) fn eval_fn_tail_call( + /// Initiate a tail call to this function -- popping the current stack frame, pushing the new + /// stack frame and initializing the arguments. + pub(super) fn init_fn_tail_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, ) -> InterpResult<'tcx> { - trace!("eval_fn_call: {:#?}", fn_val); + trace!("init_fn_tail_call: {:#?}", fn_val); // This is the "canonical" implementation of tails calls, // a pop of the current stack frame, followed by a normal call // which pushes a new stack frame, with the return address from // the popped stack frame. // - // Note that we are using `pop_stack_frame` and not `return_from_current_stack_frame`, + // Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`, // as the latter "executes" the goto to the return block, but we don't want to, // only the tail called function should return to the current return block. M::before_stack_pop(self, self.frame())?; let StackPopInfo { return_action, return_to_block, return_place } = - self.pop_stack_frame(false)?; + self.pop_stack_frame_raw(false)?; assert_eq!(return_action, ReturnAction::Normal); + // Take the "stack pop cleanup" info, and use that to initiate the next call. let StackPopCleanup::Goto { ret, unwind } = return_to_block else { bug!("can't tailcall as root"); }; @@ -981,7 +795,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // we should check if both caller&callee can/n't unwind, // see - self.eval_fn_call( + self.init_fn_call( fn_val, (caller_abi, caller_fn_abi), args, @@ -992,41 +806,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) } - fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { - // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 - let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - if !self.tcx.sess.target.is_like_wasm - && attrs - .target_features - .iter() - .any(|feature| !self.tcx.sess.target_features.contains(feature)) - { - throw_ub_custom!( - fluent::const_eval_unavailable_target_features_for_fn, - unavailable_feats = attrs - .target_features - .iter() - .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) - .fold(String::new(), |mut s, feature| { - if !s.is_empty() { - s.push_str(", "); - } - s.push_str(feature.as_str()); - s - }), - ); - } - Ok(()) - } - - fn drop_in_place( + pub(super) fn init_drop_in_place_call( &mut self, place: &PlaceTy<'tcx, M::Provenance>, instance: ty::Instance<'tcx>, target: mir::BasicBlock, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { - trace!("drop_in_place: {:?},\n instance={:?}", place, instance); + trace!("init_drop_in_place_call: {:?},\n instance={:?}", place, instance); // We take the address of the object. This may well be unaligned, which is fine // for us here. However, unaligned accesses will probably make the actual drop // implementation fail -- a problem shared by rustc. @@ -1061,7 +848,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let arg = self.mplace_to_ref(&place)?; let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); - self.eval_fn_call( + self.init_fn_call( FnVal::Instance(instance), (Abi::Rust, fn_abi), &[FnArg::Copy(arg.into())], @@ -1071,4 +858,118 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { unwind, ) } + + /// Pops the current frame from the stack, copies the return value to the caller, deallocates + /// the memory for allocated locals, and jumps to an appropriate place. + /// + /// If `unwinding` is `false`, then we are performing a normal return + /// from a function. In this case, we jump back into the frame of the caller, + /// and continue execution as normal. + /// + /// If `unwinding` is `true`, then we are in the middle of a panic, + /// and need to unwind this frame. In this case, we jump to the + /// `cleanup` block for the function, which is responsible for running + /// `Drop` impls for any locals that have been initialized at this point. + /// The cleanup block ends with a special `Resume` terminator, which will + /// cause us to continue unwinding. + #[instrument(skip(self), level = "trace")] + pub(super) fn return_from_current_stack_frame( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx> { + info!( + "popping stack frame ({})", + if unwinding { "during unwinding" } else { "returning from function" } + ); + + // Check `unwinding`. + assert_eq!( + unwinding, + match self.frame().loc { + Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, + Right(_) => true, + } + ); + if unwinding && self.frame_idx() == 0 { + throw_ub_custom!(fluent::const_eval_unwind_past_top); + } + + M::before_stack_pop(self, self.frame())?; + + // Copy return value. Must of course happen *before* we deallocate the locals. + // Must be *after* `before_stack_pop` as otherwise the return place might still be protected. + let copy_ret_result = if !unwinding { + let op = self + .local_to_op(mir::RETURN_PLACE, None) + .expect("return place should always be live"); + let dest = self.frame().return_place.clone(); + let res = if self.stack().len() == 1 { + // The initializer of constants and statics will get validated separately + // after the constant has been fully evaluated. While we could fall back to the default + // code path, that will cause -Zenforce-validity to cycle on static initializers. + // Reading from a static's memory is not allowed during its evaluation, and will always + // trigger a cycle error. Validation must read from the memory of the current item. + // For Miri this means we do not validate the root frame return value, + // but Miri anyway calls `read_target_isize` on that so separate validation + // is not needed. + self.copy_op_no_dest_validation(&op, &dest) + } else { + self.copy_op_allow_transmute(&op, &dest) + }; + trace!("return value: {:?}", self.dump_place(&dest.into())); + // We delay actually short-circuiting on this error until *after* the stack frame is + // popped, since we want this error to be attributed to the caller, whose type defines + // this transmute. + res + } else { + Ok(()) + }; + + // All right, now it is time to actually pop the frame. + let stack_pop_info = self.pop_stack_frame_raw(unwinding)?; + + // Report error from return value copy, if any. + copy_ret_result?; + + match stack_pop_info.return_action { + ReturnAction::Normal => {} + ReturnAction::NoJump => { + // The hook already did everything. + return Ok(()); + } + ReturnAction::NoCleanup => { + // If we are not doing cleanup, also skip everything else. + assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); + assert!(!unwinding, "tried to skip cleanup during unwinding"); + // Skip machine hook. + return Ok(()); + } + } + + // Normal return, figure out where to jump. + if unwinding { + // Follow the unwind edge. + match stack_pop_info.return_to_block { + StackPopCleanup::Goto { unwind, .. } => { + // This must be the very last thing that happens, since it can in fact push a new stack frame. + self.unwind_to_block(unwind) + } + StackPopCleanup::Root { .. } => { + panic!("encountered StackPopCleanup::Root when unwinding!") + } + } + } else { + // Follow the normal return edge. + match stack_pop_info.return_to_block { + StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret), + StackPopCleanup::Root { .. } => { + assert!( + self.stack().is_empty(), + "only the bottommost frame can have StackPopCleanup::Root" + ); + Ok(()) + } + } + } + } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 85f9b2341d91c..7a6bbdfdcb5e5 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,40 +1,29 @@ -use std::cell::Cell; -use std::{fmt, mem}; - -use either::{Either, Left, Right}; +use either::{Left, Right}; use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::DefId; -use rustc_hir::definitions::DefPathData; -use rustc_hir::{self as hir}; -use rustc_index::IndexVec; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; -use rustc_middle::mir::interpret::{ - CtfeProvenance, ErrorHandled, InvalidMetaKind, ReportedErrorInfo, -}; +use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ - self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers, - TyAndLayout, + self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, Variance}; -use rustc_middle::{bug, mir, span_bug}; -use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_middle::{mir, span_bug}; use rustc_session::Limit; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_trait_selection::traits::ObligationCtxt; -use tracing::{debug, info, info_span, instrument, trace}; +use tracing::{debug, trace}; use super::{ - err_inval, throw_inval, throw_ub, throw_ub_custom, throw_unsup, GlobalId, Immediate, - InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind, - OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, - ReturnAction, Scalar, + err_inval, throw_inval, throw_ub, throw_ub_custom, Frame, FrameInfo, GlobalId, InterpErrorInfo, + InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, + Projectable, Provenance, }; -use crate::{errors, fluent_generated as fluent, util, ReportErrorExt}; +use crate::{fluent_generated as fluent, util, ReportErrorExt}; pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// Stores the `Machine` instance. @@ -57,314 +46,6 @@ pub struct InterpCx<'tcx, M: Machine<'tcx>> { pub recursion_limit: Limit, } -// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread -// boundary and dropped in the other thread, it would exit the span in the other thread. -struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>); - -impl SpanGuard { - /// By default a `SpanGuard` does nothing. - fn new() -> Self { - Self(tracing::Span::none(), std::marker::PhantomData) - } - - /// If a span is entered, we exit the previous span (if any, normally none) and enter the - /// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of - /// `Frame` by creating a dummy span to being with and then entering it once the frame has - /// been pushed. - fn enter(&mut self, span: tracing::Span) { - // This executes the destructor on the previous instance of `SpanGuard`, ensuring that - // we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we - // can't protect the tracing stack, but that'll just lead to weird logging, no actual - // problems. - *self = Self(span, std::marker::PhantomData); - self.0.with_subscriber(|(id, dispatch)| { - dispatch.enter(id); - }); - } -} - -impl Drop for SpanGuard { - fn drop(&mut self) { - self.0.with_subscriber(|(id, dispatch)| { - dispatch.exit(id); - }); - } -} - -/// A stack frame. -pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { - //////////////////////////////////////////////////////////////////////////////// - // Function and callsite information - //////////////////////////////////////////////////////////////////////////////// - /// The MIR for the function called on this frame. - pub body: &'tcx mir::Body<'tcx>, - - /// The def_id and args of the current function. - pub instance: ty::Instance<'tcx>, - - /// Extra data for the machine. - pub extra: Extra, - - //////////////////////////////////////////////////////////////////////////////// - // Return place and locals - //////////////////////////////////////////////////////////////////////////////// - /// Work to perform when returning from this function. - pub return_to_block: StackPopCleanup, - - /// The location where the result of the current stack frame should be written to, - /// and its layout in the caller. - pub return_place: MPlaceTy<'tcx, Prov>, - - /// The list of locals for this stack frame, stored in order as - /// `[return_ptr, arguments..., variables..., temporaries...]`. - /// The locals are stored as `Option`s. - /// `None` represents a local that is currently dead, while a live local - /// can either directly contain `Scalar` or refer to some part of an `Allocation`. - /// - /// Do *not* access this directly; always go through the machine hook! - pub locals: IndexVec>, - - /// The span of the `tracing` crate is stored here. - /// When the guard is dropped, the span is exited. This gives us - /// a full stack trace on all tracing statements. - tracing_span: SpanGuard, - - //////////////////////////////////////////////////////////////////////////////// - // Current position within the function - //////////////////////////////////////////////////////////////////////////////// - /// If this is `Right`, we are not currently executing any particular statement in - /// this frame (can happen e.g. during frame initialization, and during unwinding on - /// frames without cleanup code). - /// - /// Needs to be public because ConstProp does unspeakable things to it. - pub loc: Either, -} - -/// What we store about a frame in an interpreter backtrace. -#[derive(Clone, Debug)] -pub struct FrameInfo<'tcx> { - pub instance: ty::Instance<'tcx>, - pub span: Span, -} - -#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these -pub enum StackPopCleanup { - /// Jump to the next block in the caller, or cause UB if None (that's a function - /// that may never return). Also store layout of return place so - /// we can validate it at that layout. - /// `ret` stores the block we jump to on a normal return, while `unwind` - /// stores the block used for cleanup during unwinding. - Goto { ret: Option, unwind: mir::UnwindAction }, - /// The root frame of the stack: nowhere else to jump to. - /// `cleanup` says whether locals are deallocated. Static computation - /// wants them leaked to intern what they need (and just throw away - /// the entire `ecx` when it is done). - Root { cleanup: bool }, -} - -/// Return type of [`InterpCx::pop_stack_frame`]. -pub struct StackPopInfo<'tcx, Prov: Provenance> { - /// Additional information about the action to be performed when returning from the popped - /// stack frame. - pub return_action: ReturnAction, - - /// [`return_to_block`](Frame::return_to_block) of the popped stack frame. - pub return_to_block: StackPopCleanup, - - /// [`return_place`](Frame::return_place) of the popped stack frame. - pub return_place: MPlaceTy<'tcx, Prov>, -} - -/// State of a local variable including a memoized layout -#[derive(Clone)] -pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { - value: LocalValue, - /// Don't modify if `Some`, this is only used to prevent computing the layout twice. - /// Avoids computing the layout of locals that are never actually initialized. - layout: Cell>>, -} - -impl std::fmt::Debug for LocalState<'_, Prov> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("LocalState") - .field("value", &self.value) - .field("ty", &self.layout.get().map(|l| l.ty)) - .finish() - } -} - -/// Current value of a local variable -/// -/// This does not store the type of the local; the type is given by `body.local_decls` and can never -/// change, so by not storing here we avoid having to maintain that as an invariant. -#[derive(Copy, Clone, Debug)] // Miri debug-prints these -pub(super) enum LocalValue { - /// This local is not currently alive, and cannot be used at all. - Dead, - /// A normal, live local. - /// Mostly for convenience, we re-use the `Operand` type here. - /// This is an optimization over just always having a pointer here; - /// we can thus avoid doing an allocation when the local just stores - /// immediate values *and* never has its address taken. - Live(Operand), -} - -impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { - pub fn make_live_uninit(&mut self) { - self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit)); - } - - /// This is a hack because Miri needs a way to visit all the provenance in a `LocalState` - /// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type - /// private. - pub fn as_mplace_or_imm( - &self, - ) -> Option>, MemPlaceMeta), Immediate>> { - match self.value { - LocalValue::Dead => None, - LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))), - LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)), - } - } - - /// Read the local's value or error if the local is not yet live or not live anymore. - #[inline(always)] - pub(super) fn access(&self) -> InterpResult<'tcx, &Operand> { - match &self.value { - LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), - } - } - - /// Overwrite the local. If the local can be overwritten in place, return a reference - /// to do so; otherwise return the `MemPlace` to consult instead. - #[inline(always)] - pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand> { - match &mut self.value { - LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? - LocalValue::Live(val) => Ok(val), - } - } -} - -impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { - pub fn with_extra(self, extra: Extra) -> Frame<'tcx, Prov, Extra> { - Frame { - body: self.body, - instance: self.instance, - return_to_block: self.return_to_block, - return_place: self.return_place, - locals: self.locals, - loc: self.loc, - extra, - tracing_span: self.tracing_span, - } - } -} - -impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { - /// Get the current location within the Frame. - /// - /// If this is `Right`, we are not currently executing any particular statement in - /// this frame (can happen e.g. during frame initialization, and during unwinding on - /// frames without cleanup code). - /// - /// Used by priroda. - pub fn current_loc(&self) -> Either { - self.loc - } - - /// Return the `SourceInfo` of the current instruction. - pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { - self.loc.left().map(|loc| self.body.source_info(loc)) - } - - pub fn current_span(&self) -> Span { - match self.loc { - Left(loc) => self.body.source_info(loc).span, - Right(span) => span, - } - } - - pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option { - // We first try to get a HirId via the current source scope, - // and fall back to `body.source`. - self.current_source_info() - .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data { - mir::ClearCrossCrate::Set(data) => Some(data.lint_root), - mir::ClearCrossCrate::Clear => None, - }) - .or_else(|| { - let def_id = self.body.source.def_id().as_local(); - def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id)) - }) - } - - /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a - /// sanity check to detect bugs where we mix up which stack frame a place refers to. - #[inline(always)] - pub(super) fn locals_addr(&self) -> usize { - self.locals.raw.as_ptr().addr() - } - - #[must_use] - pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { - let mut frames = Vec::new(); - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - for frame in stack.iter().rev() { - let span = match frame.loc { - Left(loc) => { - // If the stacktrace passes through MIR-inlined source scopes, add them. - let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); - let mut scope_data = &frame.body.source_scopes[scope]; - while let Some((instance, call_span)) = scope_data.inlined { - frames.push(FrameInfo { span, instance }); - span = call_span; - scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; - } - span - } - Right(span) => span, - }; - frames.push(FrameInfo { span, instance: frame.instance }); - } - trace!("generate stacktrace: {:#?}", frames); - frames - } -} - -// FIXME: only used by miri, should be removed once translatable. -impl<'tcx> fmt::Display for FrameInfo<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { - write!(f, "inside closure") - } else { - // Note: this triggers a `must_produce_diag` state, which means that if we ever - // get here we must emit a diagnostic. We should never display a `FrameInfo` unless - // we actually want to emit a warning or error to the user. - write!(f, "inside `{}`", self.instance) - } - }) - } -} - -impl<'tcx> FrameInfo<'tcx> { - pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote { - let span = self.span; - if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { - errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 } - } else { - let instance = format!("{}", self.instance); - // Note: this triggers a `must_produce_diag` state, which means that if we ever get - // here we must emit a diagnostic. We should never display a `FrameInfo` unless we - // actually want to emit a warning or error to the user. - errors::FrameNote { where_: "instance", span, instance, times: 0 } - } - } -} - impl<'tcx, M: Machine<'tcx>> HasDataLayout for InterpCx<'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { @@ -560,17 +241,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.frame().body } - #[inline(always)] - pub fn sign_extend(&self, value: u128, ty: TyAndLayout<'_>) -> u128 { - assert!(ty.abi.is_signed()); - ty.size.sign_extend(value) - } - - #[inline(always)] - pub fn truncate(&self, value: u128, ty: TyAndLayout<'_>) -> u128 { - ty.size.truncate(value) - } - #[inline] pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { ty.is_freeze(*self.tcx, self.param_env) @@ -714,30 +384,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") } - #[inline(always)] - pub(super) fn layout_of_local( - &self, - frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, - local: mir::Local, - layout: Option>, - ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - let state = &frame.locals[local]; - if let Some(layout) = state.layout.get() { - return Ok(layout); - } - - let layout = from_known_layout(self.tcx, self.param_env, layout, || { - let local_ty = frame.body.local_decls[local].ty; - let local_ty = - self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; - self.layout_of(local_ty) - })?; - - // Layouts of locals are requested a lot, so we cache them. - state.layout.set(Some(layout)); - Ok(layout) - } - /// Returns the actual dynamic size and alignment of the place at the given type. /// Only the "meta" (metadata) part of the place matters. /// This can fail to provide an answer for extern types. @@ -836,132 +482,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.size_and_align_of(&mplace.meta(), &mplace.layout) } - #[instrument(skip(self, body, return_place, return_to_block), level = "debug")] - pub fn push_stack_frame( - &mut self, - instance: ty::Instance<'tcx>, - body: &'tcx mir::Body<'tcx>, - return_place: &MPlaceTy<'tcx, M::Provenance>, - return_to_block: StackPopCleanup, - ) -> InterpResult<'tcx> { - trace!("body: {:#?}", body); - - // First push a stack frame so we have access to the local args - self.push_new_stack_frame(instance, body, return_to_block, return_place.clone())?; - - self.after_stack_frame_push(instance, body)?; - - Ok(()) - } - - /// Creates a new stack frame, initializes it and pushes it onto the stack. - /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). - fn push_new_stack_frame( - &mut self, - instance: ty::Instance<'tcx>, - body: &'tcx mir::Body<'tcx>, - return_to_block: StackPopCleanup, - return_place: MPlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx> { - let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; - let locals = IndexVec::from_elem(dead_local, &body.local_decls); - let pre_frame = Frame { - body, - loc: Right(body.span), // Span used for errors caused during preamble. - return_to_block, - return_place, - locals, - instance, - tracing_span: SpanGuard::new(), - extra: (), - }; - let frame = M::init_frame(self, pre_frame)?; - self.stack_mut().push(frame); - - Ok(()) - } - - /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). - fn after_stack_frame_push( - &mut self, - instance: ty::Instance<'tcx>, - body: &'tcx mir::Body<'tcx>, - ) -> InterpResult<'tcx> { - // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). - 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, const_.span).map_err(|err| { - err.emit_note(*self.tcx); - err - })?; - } - - // done - M::after_stack_push(self)?; - self.frame_mut().loc = Left(mir::Location::START); - - let span = info_span!("frame", "{}", instance); - self.frame_mut().tracing_span.enter(span); - - Ok(()) - } - - /// Pops a stack frame from the stack and returns some information about it. - /// - /// This also deallocates locals, if necessary. - /// - /// [`M::before_stack_pop`] should be called before calling this function. - /// [`M::after_stack_pop`] is called by this function automatically. - /// - /// [`M::before_stack_pop`]: Machine::before_stack_pop - /// [`M::after_stack_pop`]: Machine::after_stack_pop - pub fn pop_stack_frame( - &mut self, - unwinding: bool, - ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { - let cleanup = self.cleanup_current_frame_locals()?; - - let frame = - self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); - - let return_to_block = frame.return_to_block; - let return_place = frame.return_place.clone(); - - let return_action; - if cleanup { - return_action = M::after_stack_pop(self, frame, unwinding)?; - assert_ne!(return_action, ReturnAction::NoCleanup); - } else { - return_action = ReturnAction::NoCleanup; - }; - - Ok(StackPopInfo { return_action, return_to_block, return_place }) - } - - /// A private helper for [`pop_stack_frame`](InterpCx::pop_stack_frame). - /// Returns `true` if cleanup has been done, `false` otherwise. - fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { - // Cleanup: deallocate locals. - // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. - // We do this while the frame is still on the stack, so errors point to the callee. - let return_to_block = self.frame().return_to_block; - let cleanup = match return_to_block { - StackPopCleanup::Goto { .. } => true, - StackPopCleanup::Root { cleanup, .. } => cleanup, - }; - - if cleanup { - // We need to take the locals out, since we need to mutate while iterating. - let locals = mem::take(&mut self.frame_mut().locals); - for local in &locals { - self.deallocate_local(local.value)?; - } - } - - Ok(cleanup) - } - /// Jump to the given block. #[inline] pub fn go_to_block(&mut self, target: mir::BasicBlock) { @@ -1008,248 +528,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } - /// Pops the current frame from the stack, deallocating the - /// memory for allocated locals, and jumps to an appropriate place. - /// - /// If `unwinding` is `false`, then we are performing a normal return - /// from a function. In this case, we jump back into the frame of the caller, - /// and continue execution as normal. - /// - /// If `unwinding` is `true`, then we are in the middle of a panic, - /// and need to unwind this frame. In this case, we jump to the - /// `cleanup` block for the function, which is responsible for running - /// `Drop` impls for any locals that have been initialized at this point. - /// The cleanup block ends with a special `Resume` terminator, which will - /// cause us to continue unwinding. - #[instrument(skip(self), level = "debug")] - pub(super) fn return_from_current_stack_frame( - &mut self, - unwinding: bool, - ) -> InterpResult<'tcx> { - info!( - "popping stack frame ({})", - if unwinding { "during unwinding" } else { "returning from function" } - ); - - // Check `unwinding`. - assert_eq!( - unwinding, - match self.frame().loc { - Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, - Right(_) => true, - } - ); - if unwinding && self.frame_idx() == 0 { - throw_ub_custom!(fluent::const_eval_unwind_past_top); - } - - M::before_stack_pop(self, self.frame())?; - - // Copy return value. Must of course happen *before* we deallocate the locals. - let copy_ret_result = if !unwinding { - let op = self - .local_to_op(mir::RETURN_PLACE, None) - .expect("return place should always be live"); - let dest = self.frame().return_place.clone(); - let err = if self.stack().len() == 1 { - // The initializer of constants and statics will get validated separately - // after the constant has been fully evaluated. While we could fall back to the default - // code path, that will cause -Zenforce-validity to cycle on static initializers. - // Reading from a static's memory is not allowed during its evaluation, and will always - // trigger a cycle error. Validation must read from the memory of the current item. - // For Miri this means we do not validate the root frame return value, - // but Miri anyway calls `read_target_isize` on that so separate validation - // is not needed. - self.copy_op_no_dest_validation(&op, &dest) - } else { - self.copy_op_allow_transmute(&op, &dest) - }; - trace!("return value: {:?}", self.dump_place(&dest.into())); - // We delay actually short-circuiting on this error until *after* the stack frame is - // popped, since we want this error to be attributed to the caller, whose type defines - // this transmute. - err - } else { - Ok(()) - }; - - // All right, now it is time to actually pop the frame. - let stack_pop_info = self.pop_stack_frame(unwinding)?; - - // Report error from return value copy, if any. - copy_ret_result?; - - match stack_pop_info.return_action { - ReturnAction::Normal => {} - ReturnAction::NoJump => { - // The hook already did everything. - return Ok(()); - } - ReturnAction::NoCleanup => { - // If we are not doing cleanup, also skip everything else. - assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); - assert!(!unwinding, "tried to skip cleanup during unwinding"); - // Skip machine hook. - return Ok(()); - } - } - - // Normal return, figure out where to jump. - if unwinding { - // Follow the unwind edge. - let unwind = match stack_pop_info.return_to_block { - StackPopCleanup::Goto { unwind, .. } => unwind, - StackPopCleanup::Root { .. } => { - panic!("encountered StackPopCleanup::Root when unwinding!") - } - }; - // This must be the very last thing that happens, since it can in fact push a new stack frame. - self.unwind_to_block(unwind) - } else { - // Follow the normal return edge. - match stack_pop_info.return_to_block { - StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret), - StackPopCleanup::Root { .. } => { - assert!( - self.stack().is_empty(), - "only the topmost frame can have StackPopCleanup::Root" - ); - Ok(()) - } - } - } - } - - /// In the current stack frame, mark all locals as live that are not arguments and don't have - /// `Storage*` annotations (this includes the return place). - pub fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> { - self.storage_live(mir::RETURN_PLACE)?; - - let body = self.body(); - let always_live = always_storage_live_locals(body); - for local in body.vars_and_temps_iter() { - if always_live.contains(local) { - self.storage_live(local)?; - } - } - Ok(()) - } - - pub fn storage_live_dyn( - &mut self, - local: mir::Local, - meta: MemPlaceMeta, - ) -> InterpResult<'tcx> { - trace!("{:?} is now live", local); - - // We avoid `ty.is_trivially_sized` since that does something expensive for ADTs. - fn is_very_trivially_sized(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::RawPtr(..) - | ty::Char - | ty::Ref(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Array(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Never - | ty::Error(_) - | ty::Dynamic(_, _, ty::DynStar) => true, - - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - - ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), - - ty::Pat(ty, ..) => is_very_trivially_sized(*ty), - - // We don't want to do any queries, so there is not much we can do with ADTs. - ty::Adt(..) => false, - - ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false, - - ty::Infer(ty::TyVar(_)) => false, - - ty::Bound(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty) - } - } - } - - // This is a hot function, we avoid computing the layout when possible. - // `unsized_` will be `None` for sized types and `Some(layout)` for unsized types. - let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) { - None - } else { - // We need the layout. - let layout = self.layout_of_local(self.frame(), local, None)?; - if layout.is_sized() { None } else { Some(layout) } - }; - - let local_val = LocalValue::Live(if let Some(layout) = unsized_ { - if !meta.has_meta() { - throw_unsup!(UnsizedLocal); - } - // Need to allocate some memory, since `Immediate::Uninit` cannot be unsized. - let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?; - Operand::Indirect(*dest_place.mplace()) - } else { - assert!(!meta.has_meta()); // we're dropping the metadata - // Just make this an efficient immediate. - // Note that not calling `layout_of` here does have one real consequence: - // if the type is too big, we'll only notice this when the local is actually initialized, - // which is a bit too late -- we should ideally notice this already here, when the memory - // is conceptually allocated. But given how rare that error is and that this is a hot function, - // we accept this downside for now. - Operand::Immediate(Immediate::Uninit) - }); - - // If the local is already live, deallocate its old memory. - let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); - self.deallocate_local(old)?; - Ok(()) - } - - /// Mark a storage as live, killing the previous content. - #[inline(always)] - pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> { - self.storage_live_dyn(local, MemPlaceMeta::None) - } - - pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> { - assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); - trace!("{:?} is now dead", local); - - // If the local is already dead, this is a NOP. - let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); - self.deallocate_local(old)?; - Ok(()) - } - - #[instrument(skip(self), level = "debug")] - fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { - if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { - // All locals have a backing allocation, even if the allocation is empty - // due to the local having ZST type. Hence we can `unwrap`. - trace!( - "deallocating local {:?}: {:?}", - local, - // Locals always have a `alloc_id` (they are never the result of a int2ptr). - self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) - ); - self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; - }; - Ok(()) - } - /// Call a query that can return `ErrorHandled`. Should be used for statics and other globals. /// (`mir::Const`/`ty::Const` have `eval` methods that can be used directly instead.) pub fn ctfe_query( @@ -1339,39 +617,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for PlacePrinter<'a, 'tcx, M> { } write!(fmt, ":")?; - match self.ecx.frame().locals[local].value { - LocalValue::Dead => write!(fmt, " is dead")?, - LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { - write!(fmt, " is uninitialized")? - } - LocalValue::Live(Operand::Indirect(mplace)) => { - write!( - fmt, - " by {} ref {:?}:", - match mplace.meta { - MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"), - MemPlaceMeta::None => String::new(), - }, - mplace.ptr, - )?; - allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id)); - } - LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { - write!(fmt, " {val:?}")?; - if let Scalar::Ptr(ptr, _size) = val { - allocs.push(ptr.provenance.get_alloc_id()); - } - } - LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { - write!(fmt, " ({val1:?}, {val2:?})")?; - if let Scalar::Ptr(ptr, _size) = val1 { - allocs.push(ptr.provenance.get_alloc_id()); - } - if let Scalar::Ptr(ptr, _size) = val2 { - allocs.push(ptr.provenance.get_alloc_id()); - } - } - } + self.ecx.frame().locals[local].print(&mut allocs, fmt)?; write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs.into_iter().flatten().collect())) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 1e3de224380b9..9210ec4e16fd0 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -97,7 +97,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own /// intrinsic handling. - pub fn emulate_intrinsic( + pub fn eval_intrinsic( &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, M::Provenance>], @@ -206,7 +206,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else { (val_bits >> shift_bits) | (val_bits << inv_shift_bits) }; - let truncated_bits = self.truncate(result_bits, layout_val); + let truncated_bits = layout_val.size.truncate(result_bits); let result = Scalar::from_uint(truncated_bits, layout_val.size); self.write_scalar(result, dest)?; } @@ -243,7 +243,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (a_offset, b_offset, is_addr) = if M::Provenance::OFFSET_IS_ADDR { (a.addr().bytes(), b.addr().bytes(), /*is_addr*/ true) } else { - match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) { + match (self.ptr_try_get_alloc_id(a, 0), self.ptr_try_get_alloc_id(b, 0)) { (Err(a), Err(b)) => { // Neither pointer points to an allocation, so they are both absolute. (a, b, /*is_addr*/ true) @@ -312,7 +312,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Check that the memory between them is dereferenceable at all, starting from the - // base pointer: `dist` is `a - b`, so it is based on `b`. + // origin pointer: `dist` is `a - b`, so it is based on `b`. self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)?; // Then check that this is also dereferenceable from `a`. This ensures that they are // derived from the same allocation. @@ -447,7 +447,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(true) } - pub(super) fn emulate_nondiverging_intrinsic( + pub(super) fn eval_nondiverging_intrinsic( &mut self, intrinsic: &NonDivergingIntrinsic<'tcx>, ) -> InterpResult<'tcx> { @@ -580,13 +580,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ptr: Pointer>, offset_bytes: i64, ) -> InterpResult<'tcx, Pointer>> { - // We first compute the pointer with overflow checks, to get a specific error for when it - // overflows (though technically this is redundant with the following inbounds check). - let result = ptr.signed_offset(offset_bytes, self)?; // The offset must be in bounds starting from `ptr`. self.check_ptr_access_signed(ptr, offset_bytes, CheckInAllocMsg::PointerArithmeticTest)?; - // Done. - Ok(result) + // This also implies that there is no overflow, so we are done. + Ok(ptr.wrapping_signed_offset(offset_bytes, self)) } /// Copy `count*size_of::()` many bytes from `*src` to `*dst`. @@ -693,9 +690,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // zero-sized access return Ok(&[]); }; - if alloc_ref.has_provenance() { - throw_ub_custom!(fluent::const_eval_raw_eq_with_provenance); - } alloc_ref.get_bytes_strip_provenance() }; diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index a82209514ec8f..7af4e0c285b0b 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -37,7 +37,7 @@ pub enum ReturnAction { /// took care of everything. NoJump, - /// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done. + /// Returned by [`InterpCx::pop_stack_frame_raw`] when no cleanup should be done. NoCleanup, } @@ -165,6 +165,13 @@ pub trait Machine<'tcx>: Sized { /// Whether to enforce the validity invariant for a specific layout. fn enforce_validity(ecx: &InterpCx<'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool; + /// Whether to enforce the validity invariant *recursively*. + fn enforce_validity_recursively( + _ecx: &InterpCx<'tcx, Self>, + _layout: TyAndLayout<'tcx>, + ) -> bool { + false + } /// Whether function calls should be [ABI](CallAbi)-checked. fn enforce_abi(_ecx: &InterpCx<'tcx, Self>) -> bool { @@ -321,15 +328,21 @@ pub trait Machine<'tcx>: Sized { ptr: Pointer, ) -> InterpResult<'tcx>; - /// Convert a pointer with provenance into an allocation-offset pair - /// and extra provenance info. + /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info. + /// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can + /// be used to disambiguate situations where a wildcard pointer sits right in between two + /// allocations. /// - /// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`. + /// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`. + /// The resulting `AllocId` will just be used for that one step and the forgotten again + /// (i.e., we'll never turn the data returned here back into a `Pointer` that might be + /// stored in machine state). /// /// When this fails, that means the pointer does not point to a live allocation. fn ptr_get_alloc( ecx: &InterpCx<'tcx, Self>, ptr: Pointer, + size: i64, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>; /// Called to adjust global allocations to the Provenance and AllocExtra of this machine. @@ -658,6 +671,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { fn ptr_get_alloc( _ecx: &InterpCx<$tcx, Self>, ptr: Pointer, + _size: i64, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (prov, offset) = ptr.into_parts(); diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 859f30137dc4e..2e5d0ae773654 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -261,7 +261,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { new_align: Align, kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { - let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { throw_ub_custom!( fluent::const_eval_realloc_or_alloc_with_offset, @@ -291,7 +291,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { old_size_and_align: Option<(Size, Align)>, kind: MemoryKind, ) -> InterpResult<'tcx> { - let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, prov) = self.ptr_get_alloc_id(ptr, 0)?; trace!("deallocating: {alloc_id:?}"); if offset.bytes() != 0 { @@ -383,6 +383,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ptr: Pointer>, size: Size, ) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> { + let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes self.check_and_deref_ptr( ptr, size, @@ -404,6 +405,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: Size, msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { + let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| { let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?; Ok((size, align, ())) @@ -420,19 +422,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: i64, msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { - if let Ok(size) = u64::try_from(size) { - self.check_ptr_access(ptr, Size::from_bytes(size), msg) - } else { - // Compute the pointer at the beginning of the range, and do the standard - // dereferenceability check from there. - let begin_ptr = ptr.wrapping_signed_offset(size, self); - self.check_ptr_access(begin_ptr, Size::from_bytes(size.unsigned_abs()), msg) - } + self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| { + let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?; + Ok((size, align, ())) + })?; + Ok(()) } /// Low-level helper function to check if a ptr is in-bounds and potentially return a reference /// to the allocation it points to. Supports both shared and mutable references, as the actual - /// checking is offloaded to a helper closure. + /// checking is offloaded to a helper closure. Supports signed sizes for checks "to the left" of + /// a pointer. /// /// `alloc_size` will only get called for non-zero-sized accesses. /// @@ -440,7 +440,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn check_and_deref_ptr( &self, ptr: Pointer>, - size: Size, + size: i64, msg: CheckInAllocMsg, alloc_size: impl FnOnce( AllocId, @@ -449,24 +449,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option> { // Everything is okay with size 0. - if size.bytes() == 0 { + if size == 0 { return Ok(None); } - Ok(match self.ptr_try_get_alloc_id(ptr) { + Ok(match self.ptr_try_get_alloc_id(ptr, size) { Err(addr) => { // We couldn't get a proper allocation. throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg }); } Ok((alloc_id, offset, prov)) => { let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; - // Test bounds. - // It is sufficient to check this for the end pointer. Also check for overflow! - if offset.checked_add(size, &self.tcx).is_none_or(|end| end > alloc_size) { + let offset = offset.bytes(); + // Compute absolute begin and end of the range. + let (begin, end) = if size >= 0 { + (Some(offset), offset.checked_add(size as u64)) + } else { + (offset.checked_sub(size.unsigned_abs()), Some(offset)) + }; + // Ensure both are within bounds. + let in_bounds = begin.is_some() && end.is_some_and(|e| e <= alloc_size.bytes()); + if !in_bounds { throw_ub!(PointerOutOfBounds { alloc_id, alloc_size, - ptr_offset: self.target_usize_to_isize(offset.bytes()), + ptr_offset: self.sign_extend_to_target_isize(offset), inbounds_size: size, msg, }) @@ -498,7 +505,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } #[inline] - fn offset_misalignment(offset: u64, align: Align) -> Option { + fn is_offset_misaligned(offset: u64, align: Align) -> Option { if offset % align.bytes() == 0 { None } else { @@ -508,8 +515,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - match self.ptr_try_get_alloc_id(ptr) { - Err(addr) => offset_misalignment(addr, align), + match self.ptr_try_get_alloc_id(ptr, 0) { + Err(addr) => is_offset_misaligned(addr, align), Ok((alloc_id, offset, _prov)) => { let (_size, alloc_align, kind) = self.get_alloc_info(alloc_id); if let Some(misalign) = @@ -517,14 +524,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { { Some(misalign) } else if M::Provenance::OFFSET_IS_ADDR { - // `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true. - offset_misalignment(ptr.addr().bytes(), align) + is_offset_misaligned(ptr.addr().bytes(), align) } else { // Check allocation alignment and offset alignment. if alloc_align.bytes() < align.bytes() { Some(Misalignment { has: alloc_align, required: align }) } else { - offset_misalignment(offset.bytes(), align) + is_offset_misaligned(offset.bytes(), align) } } } @@ -660,9 +666,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: Size, ) -> InterpResult<'tcx, Option>> { + let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes let ptr_and_alloc = self.check_and_deref_ptr( ptr, - size, + size_i64, CheckInAllocMsg::MemoryAccessTest, |alloc_id, offset, prov| { let alloc = self.get_alloc_raw(alloc_id)?; @@ -673,7 +680,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // accesses. That means we cannot rely on the closure above or the `Some` branch below. We // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. if !self.memory.validation_in_progress.get() { - if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) { + if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) { M::before_alloc_read(self, alloc_id)?; } } @@ -894,7 +901,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ptr: Pointer>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { trace!("get_ptr_fn({:?})", ptr); - let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) } @@ -910,7 +917,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { expected_trait: Option<&'tcx ty::List>>, ) -> InterpResult<'tcx, Ty<'tcx>> { trace!("get_ptr_vtable({:?})", ptr); - let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) } @@ -999,8 +1006,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }) } - /// Runs the close in "validation" mode, which means the machine's memory read hooks will be + /// Runs the closure in "validation" mode, which means the machine's memory read hooks will be /// suppressed. Needless to say, this must only be set with great care! Cannot be nested. + /// + /// We do this so Miri's allocation access tracking does not show the validation + /// reads as spurious accesses. pub(super) fn run_for_validation(&self, f: impl FnOnce() -> R) -> R { // This deliberately uses `==` on `bool` to follow the pattern // `assert!(val.replace(new) == old)`. @@ -1391,7 +1401,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Err(_) => { // Can only happen during CTFE. let ptr = scalar.to_pointer(self)?; - match self.ptr_try_get_alloc_id(ptr) { + match self.ptr_try_get_alloc_id(ptr, 0) { Ok((alloc_id, offset, _)) => { let (size, _align, _kind) = self.get_alloc_info(alloc_id); // If the pointer is out-of-bounds, it may be null. @@ -1407,6 +1417,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Turning a "maybe pointer" into a proper pointer (and some information /// about where it points), or an absolute address. /// + /// `size` says how many bytes of memory are expected at that pointer. This is largely only used + /// for error messages; however, the *sign* of `size` can be used to disambiguate situations + /// where a wildcard pointer sits right in between two allocations. + /// It is almost always okay to just set the size to 0; this will be treated like a positive size + /// for handling wildcard pointers. + /// /// The result must be used immediately; it is not allowed to convert /// the returned data back into a `Pointer` and store that in machine state. /// (In fact that's not even possible since `M::ProvenanceExtra` is generic and @@ -1414,9 +1430,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn ptr_try_get_alloc_id( &self, ptr: Pointer>, + size: i64, ) -> Result<(AllocId, Size, M::ProvenanceExtra), u64> { match ptr.into_pointer_or_addr() { - Ok(ptr) => match M::ptr_get_alloc(self, ptr) { + Ok(ptr) => match M::ptr_get_alloc(self, ptr, size) { Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)), None => { assert!(M::Provenance::OFFSET_IS_ADDR); @@ -1430,6 +1447,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Turning a "maybe pointer" into a proper pointer (and some information about where it points). /// + /// `size` says how many bytes of memory are expected at that pointer. This is largely only used + /// for error messages; however, the *sign* of `size` can be used to disambiguate situations + /// where a wildcard pointer sits right in between two allocations. + /// It is almost always okay to just set the size to 0; this will be treated like a positive size + /// for handling wildcard pointers. + /// /// The result must be used immediately; it is not allowed to convert /// the returned data back into a `Pointer` and store that in machine state. /// (In fact that's not even possible since `M::ProvenanceExtra` is generic and @@ -1438,12 +1461,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn ptr_get_alloc_id( &self, ptr: Pointer>, + size: i64, ) -> InterpResult<'tcx, (AllocId, Size, M::ProvenanceExtra)> { - self.ptr_try_get_alloc_id(ptr).map_err(|offset| { + self.ptr_try_get_alloc_id(ptr, size).map_err(|offset| { err_ub!(DanglingIntPointer { addr: offset, - // We don't know the actually required size. - inbounds_size: Size::ZERO, + inbounds_size: size, msg: CheckInAllocMsg::InboundsTest }) .into() diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index afa2303e38715..511756e3f86c9 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -1,5 +1,6 @@ //! An interpreter for MIR used in CTFE and by miri +mod call; mod cast; mod discriminant; mod eval_context; @@ -11,8 +12,8 @@ mod operand; mod operator; mod place; mod projection; +mod stack; mod step; -mod terminator; mod traits; mod util; mod validity; @@ -22,7 +23,8 @@ use eval_context::{from_known_layout, mir_assign_valid_types}; #[doc(no_inline)] pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here -pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup}; +pub use self::call::FnArg; +pub use self::eval_context::{format_interp_error, InterpCx}; pub use self::intern::{ intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind, InternResult, @@ -35,7 +37,7 @@ pub use self::operand::{ImmTy, Immediate, OpTy, Readable}; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; -pub use self::terminator::FnArg; +pub use self::stack::{Frame, FrameInfo, LocalState, StackPopCleanup, StackPopInfo}; pub(crate) use self::util::create_static_alloc; pub use self::validity::{CtfeValidationMode, RefTracking}; pub use self::visitor::ValueVisitor; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index d4559e1e8c64e..4ced009ab39b6 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -184,6 +184,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { debug_assert!(layout.abi.is_scalar(), "`ImmTy::from_scalar` on non-scalar layout"); + debug_assert_eq!(val.size(), layout.size); ImmTy { imm: val.into(), layout } } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 684c98f60612f..2f860f9f942b2 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -1,13 +1,14 @@ use either::Either; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::interpret::{InterpResult, Scalar}; +use rustc_middle::mir::NullOp; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, FloatTy, ScalarInt}; +use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::symbol::sym; use tracing::trace; -use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta}; +use super::{throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn three_way_compare(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> { @@ -298,17 +299,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Pointer ops that are always supported. Offset => { let ptr = left.to_scalar().to_pointer(self)?; - let offset_count = right.to_scalar().to_target_isize(self)?; let pointee_ty = left.layout.ty.builtin_deref(true).unwrap(); + let pointee_layout = self.layout_of(pointee_ty)?; + assert!(pointee_layout.abi.is_sized()); // We cannot overflow i64 as a type's size must be <= isize::MAX. - let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); - // The computed offset, in bytes, must not overflow an isize. - // `checked_mul` enforces a too small bound, but no actual allocation can be big enough for - // the difference to be noticeable. - let offset_bytes = - offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; + let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap(); + let pointee_size = ImmTy::from_int(pointee_size, right.layout); + // Multiply element size and element count. + let (val, overflowed) = self + .binary_op(mir::BinOp::MulWithOverflow, right, &pointee_size)? + .to_scalar_pair(); + // This must not overflow. + if overflowed.to_bool()? { + throw_ub!(PointerArithOverflow) + } + let offset_bytes = val.to_target_isize(self)?; let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?; Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout)) } @@ -329,11 +336,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { trace!( "Running binary op {:?}: {:?} ({}), {:?} ({})", - bin_op, - *left, - left.layout.ty, - *right, - right.layout.ty + bin_op, *left, left.layout.ty, *right, right.layout.ty ); match left.layout.ty.kind() { @@ -478,4 +481,38 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } } + + pub fn nullary_op( + &self, + null_op: NullOp<'tcx>, + arg_ty: Ty<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { + use rustc_middle::mir::NullOp::*; + + let layout = self.layout_of(arg_ty)?; + let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap(); + + Ok(match null_op { + SizeOf => { + if !layout.abi.is_sized() { + span_bug!(self.cur_span(), "unsized type for `NullaryOp::SizeOf`"); + } + let val = layout.size.bytes(); + ImmTy::from_uint(val, usize_layout()) + } + AlignOf => { + if !layout.abi.is_sized() { + span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`"); + } + let val = layout.align.abi.bytes(); + ImmTy::from_uint(val, usize_layout()) + } + OffsetOf(fields) => { + let val = + self.tcx.offset_of_subfield(self.param_env, layout, fields.iter()).bytes(); + ImmTy::from_uint(val, usize_layout()) + } + UbChecks => ImmTy::from_bool(self.tcx.sess.ub_checks(), *self.tcx), + }) + } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 9f79f4c55be85..470a62026b943 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -13,10 +13,9 @@ use rustc_target::abi::{Abi, Align, HasDataLayout, Size}; use tracing::{instrument, trace}; use super::{ - alloc_range, mir_assign_valid_types, throw_ub, AllocRef, AllocRefMut, CheckAlignMsg, - CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, - OffsetMode, OpTy, Operand, Pointer, PointerArithmetic, Projectable, Provenance, Readable, - Scalar, + alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, + ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, + Operand, Pointer, Projectable, Provenance, Readable, Scalar, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -85,9 +84,6 @@ impl MemPlace { !meta.has_meta() || self.meta.has_meta(), "cannot use `offset_with_meta` to add metadata to a place" ); - if offset > ecx.data_layout().max_size_of_val() { - throw_ub!(PointerArithOverflow); - } let ptr = match mode { OffsetMode::Inbounds => { ecx.ptr_offset_inbounds(self.ptr, offset.bytes().try_into().unwrap())? @@ -289,10 +285,8 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { // projections are type-checked and bounds-checked. assert!(offset + layout.size <= self.layout.size); - let new_offset = Size::from_bytes( - ecx.data_layout() - .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?, - ); + // Size `+`, ensures no overflow. + let new_offset = old_offset.unwrap_or(Size::ZERO) + offset; PlaceTy { place: Place::Local { local, offset: Some(new_offset), locals_addr }, @@ -578,7 +572,10 @@ where if M::enforce_validity(self, dest.layout()) { // Data got changed, better make sure it matches the type! - self.validate_operand(&dest.to_op(self)?)?; + self.validate_operand( + &dest.to_op(self)?, + M::enforce_validity_recursively(self, dest.layout()), + )?; } Ok(()) @@ -817,7 +814,10 @@ where // Generally for transmutation, data must be valid both at the old and new type. // But if the types are the same, the 2nd validation below suffices. if src.layout().ty != dest.layout().ty && M::enforce_validity(self, src.layout()) { - self.validate_operand(&src.to_op(self)?)?; + self.validate_operand( + &src.to_op(self)?, + M::enforce_validity_recursively(self, src.layout()), + )?; } // Do the actual copy. @@ -825,7 +825,10 @@ where if validate_dest && M::enforce_validity(self, dest.layout()) { // Data got changed, better make sure it matches the type! - self.validate_operand(&dest.to_op(self)?)?; + self.validate_operand( + &dest.to_op(self)?, + M::enforce_validity_recursively(self, dest.layout()), + )?; } Ok(()) diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs new file mode 100644 index 0000000000000..50dbced6a2a6a --- /dev/null +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -0,0 +1,651 @@ +//! Manages the low-level pushing and popping of stack frames and the (de)allocation of local variables. +//! For hadling of argument passing and return values, see the `call` module. +use std::cell::Cell; +use std::{fmt, mem}; + +use either::{Either, Left, Right}; +use rustc_hir as hir; +use rustc_hir::definitions::DefPathData; +use rustc_index::IndexVec; +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, mir}; +use rustc_mir_dataflow::storage::always_storage_live_locals; +use rustc_span::Span; +use tracing::{info_span, instrument, trace}; + +use super::{ + from_known_layout, throw_ub, throw_unsup, AllocId, CtfeProvenance, Immediate, InterpCx, + InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, MemoryKind, Operand, Pointer, + Provenance, ReturnAction, Scalar, +}; +use crate::errors; + +// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread +// boundary and dropped in the other thread, it would exit the span in the other thread. +struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>); + +impl SpanGuard { + /// By default a `SpanGuard` does nothing. + fn new() -> Self { + Self(tracing::Span::none(), std::marker::PhantomData) + } + + /// If a span is entered, we exit the previous span (if any, normally none) and enter the + /// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of + /// `Frame` by creating a dummy span to being with and then entering it once the frame has + /// been pushed. + fn enter(&mut self, span: tracing::Span) { + // This executes the destructor on the previous instance of `SpanGuard`, ensuring that + // we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we + // can't protect the tracing stack, but that'll just lead to weird logging, no actual + // problems. + *self = Self(span, std::marker::PhantomData); + self.0.with_subscriber(|(id, dispatch)| { + dispatch.enter(id); + }); + } +} + +impl Drop for SpanGuard { + fn drop(&mut self) { + self.0.with_subscriber(|(id, dispatch)| { + dispatch.exit(id); + }); + } +} + +/// A stack frame. +pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { + //////////////////////////////////////////////////////////////////////////////// + // Function and callsite information + //////////////////////////////////////////////////////////////////////////////// + /// The MIR for the function called on this frame. + pub(super) body: &'tcx mir::Body<'tcx>, + + /// The def_id and args of the current function. + pub(super) instance: ty::Instance<'tcx>, + + /// Extra data for the machine. + pub extra: Extra, + + //////////////////////////////////////////////////////////////////////////////// + // Return place and locals + //////////////////////////////////////////////////////////////////////////////// + /// Work to perform when returning from this function. + return_to_block: StackPopCleanup, + + /// The location where the result of the current stack frame should be written to, + /// and its layout in the caller. + pub return_place: MPlaceTy<'tcx, Prov>, + + /// The list of locals for this stack frame, stored in order as + /// `[return_ptr, arguments..., variables..., temporaries...]`. + /// The locals are stored as `Option`s. + /// `None` represents a local that is currently dead, while a live local + /// can either directly contain `Scalar` or refer to some part of an `Allocation`. + /// + /// Do *not* access this directly; always go through the machine hook! + pub locals: IndexVec>, + + /// The span of the `tracing` crate is stored here. + /// When the guard is dropped, the span is exited. This gives us + /// a full stack trace on all tracing statements. + tracing_span: SpanGuard, + + //////////////////////////////////////////////////////////////////////////////// + // Current position within the function + //////////////////////////////////////////////////////////////////////////////// + /// If this is `Right`, we are not currently executing any particular statement in + /// this frame (can happen e.g. during frame initialization, and during unwinding on + /// frames without cleanup code). + /// + /// Needs to be public because ConstProp does unspeakable things to it. + pub(super) loc: Either, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these +pub enum StackPopCleanup { + /// Jump to the next block in the caller, or cause UB if None (that's a function + /// that may never return). Also store layout of return place so + /// we can validate it at that layout. + /// `ret` stores the block we jump to on a normal return, while `unwind` + /// stores the block used for cleanup during unwinding. + Goto { ret: Option, unwind: mir::UnwindAction }, + /// The root frame of the stack: nowhere else to jump to. + /// `cleanup` says whether locals are deallocated. Static computation + /// wants them leaked to intern what they need (and just throw away + /// the entire `ecx` when it is done). + Root { cleanup: bool }, +} + +/// Return type of [`InterpCx::pop_stack_frame_raw`]. +pub struct StackPopInfo<'tcx, Prov: Provenance> { + /// Additional information about the action to be performed when returning from the popped + /// stack frame. + pub return_action: ReturnAction, + + /// [`return_to_block`](Frame::return_to_block) of the popped stack frame. + pub return_to_block: StackPopCleanup, + + /// [`return_place`](Frame::return_place) of the popped stack frame. + pub return_place: MPlaceTy<'tcx, Prov>, +} + +/// State of a local variable including a memoized layout +#[derive(Clone)] +pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { + value: LocalValue, + /// Don't modify if `Some`, this is only used to prevent computing the layout twice. + /// Avoids computing the layout of locals that are never actually initialized. + layout: Cell>>, +} + +impl std::fmt::Debug for LocalState<'_, Prov> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LocalState") + .field("value", &self.value) + .field("ty", &self.layout.get().map(|l| l.ty)) + .finish() + } +} + +/// Current value of a local variable +/// +/// This does not store the type of the local; the type is given by `body.local_decls` and can never +/// change, so by not storing here we avoid having to maintain that as an invariant. +#[derive(Copy, Clone, Debug)] // Miri debug-prints these +pub(super) enum LocalValue { + /// This local is not currently alive, and cannot be used at all. + Dead, + /// A normal, live local. + /// Mostly for convenience, we re-use the `Operand` type here. + /// This is an optimization over just always having a pointer here; + /// we can thus avoid doing an allocation when the local just stores + /// immediate values *and* never has its address taken. + Live(Operand), +} + +impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { + pub fn make_live_uninit(&mut self) { + self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit)); + } + + /// This is a hack because Miri needs a way to visit all the provenance in a `LocalState` + /// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type + /// private. + pub fn as_mplace_or_imm( + &self, + ) -> Option>, MemPlaceMeta), Immediate>> { + match self.value { + LocalValue::Dead => None, + LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))), + LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)), + } + } + + /// Read the local's value or error if the local is not yet live or not live anymore. + #[inline(always)] + pub(super) fn access(&self) -> InterpResult<'tcx, &Operand> { + match &self.value { + LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? + LocalValue::Live(val) => Ok(val), + } + } + + /// Overwrite the local. If the local can be overwritten in place, return a reference + /// to do so; otherwise return the `MemPlace` to consult instead. + #[inline(always)] + pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand> { + match &mut self.value { + LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"? + LocalValue::Live(val) => Ok(val), + } + } +} + +/// What we store about a frame in an interpreter backtrace. +#[derive(Clone, Debug)] +pub struct FrameInfo<'tcx> { + pub instance: ty::Instance<'tcx>, + pub span: Span, +} + +// FIXME: only used by miri, should be removed once translatable. +impl<'tcx> fmt::Display for FrameInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { + write!(f, "inside closure") + } else { + // Note: this triggers a `must_produce_diag` state, which means that if we ever + // get here we must emit a diagnostic. We should never display a `FrameInfo` unless + // we actually want to emit a warning or error to the user. + write!(f, "inside `{}`", self.instance) + } + }) + } +} + +impl<'tcx> FrameInfo<'tcx> { + pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote { + let span = self.span; + if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { + errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 } + } else { + let instance = format!("{}", self.instance); + // Note: this triggers a `must_produce_diag` state, which means that if we ever get + // here we must emit a diagnostic. We should never display a `FrameInfo` unless we + // actually want to emit a warning or error to the user. + errors::FrameNote { where_: "instance", span, instance, times: 0 } + } + } +} + +impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { + pub fn with_extra(self, extra: Extra) -> Frame<'tcx, Prov, Extra> { + Frame { + body: self.body, + instance: self.instance, + return_to_block: self.return_to_block, + return_place: self.return_place, + locals: self.locals, + loc: self.loc, + extra, + tracing_span: self.tracing_span, + } + } +} + +impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { + /// Get the current location within the Frame. + /// + /// If this is `Right`, we are not currently executing any particular statement in + /// this frame (can happen e.g. during frame initialization, and during unwinding on + /// frames without cleanup code). + /// + /// Used by [priroda](https://github.com/oli-obk/priroda). + pub fn current_loc(&self) -> Either { + self.loc + } + + pub fn body(&self) -> &'tcx mir::Body<'tcx> { + self.body + } + + pub fn instance(&self) -> ty::Instance<'tcx> { + self.instance + } + + /// Return the `SourceInfo` of the current instruction. + pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { + self.loc.left().map(|loc| self.body.source_info(loc)) + } + + pub fn current_span(&self) -> Span { + match self.loc { + Left(loc) => self.body.source_info(loc).span, + Right(span) => span, + } + } + + pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option { + // We first try to get a HirId via the current source scope, + // and fall back to `body.source`. + self.current_source_info() + .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data { + mir::ClearCrossCrate::Set(data) => Some(data.lint_root), + mir::ClearCrossCrate::Clear => None, + }) + .or_else(|| { + let def_id = self.body.source.def_id().as_local(); + def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id)) + }) + } + + /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a + /// sanity check to detect bugs where we mix up which stack frame a place refers to. + #[inline(always)] + pub(super) fn locals_addr(&self) -> usize { + self.locals.raw.as_ptr().addr() + } + + #[must_use] + pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { + let mut frames = Vec::new(); + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + for frame in stack.iter().rev() { + let span = match frame.loc { + Left(loc) => { + // If the stacktrace passes through MIR-inlined source scopes, add them. + let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); + let mut scope_data = &frame.body.source_scopes[scope]; + while let Some((instance, call_span)) = scope_data.inlined { + frames.push(FrameInfo { span, instance }); + span = call_span; + scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; + } + span + } + Right(span) => span, + }; + frames.push(FrameInfo { span, instance: frame.instance }); + } + trace!("generate stacktrace: {:#?}", frames); + frames + } +} + +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Very low-level helper that pushes a stack frame without initializing + /// the arguments or local variables. + /// + /// The high-level version of this is `init_stack_frame`. + #[instrument(skip(self, body, return_place, return_to_block), level = "debug")] + pub(crate) fn push_stack_frame_raw( + &mut self, + instance: ty::Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + return_place: &MPlaceTy<'tcx, M::Provenance>, + return_to_block: StackPopCleanup, + ) -> InterpResult<'tcx> { + trace!("body: {:#?}", body); + + // We can push a `Root` frame if and only if the stack is empty. + debug_assert_eq!( + self.stack().is_empty(), + matches!(return_to_block, StackPopCleanup::Root { .. }) + ); + + // First push a stack frame so we have access to `instantiate_from_current_frame` and other + // `self.frame()`-based functions. + let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; + let locals = IndexVec::from_elem(dead_local, &body.local_decls); + let pre_frame = Frame { + body, + loc: Right(body.span), // Span used for errors caused during preamble. + return_to_block, + return_place: return_place.clone(), + locals, + instance, + tracing_span: SpanGuard::new(), + extra: (), + }; + let frame = M::init_frame(self, pre_frame)?; + self.stack_mut().push(frame); + + // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). + 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, const_.span).map_err(|err| { + err.emit_note(*self.tcx); + err + })?; + } + + // Finish things up. + M::after_stack_push(self)?; + self.frame_mut().loc = Left(mir::Location::START); + let span = info_span!("frame", "{}", instance); + self.frame_mut().tracing_span.enter(span); + + Ok(()) + } + + /// Low-level helper that pops a stack frame from the stack and returns some information about + /// it. + /// + /// This also deallocates locals, if necessary. + /// + /// [`M::before_stack_pop`] should be called before calling this function. + /// [`M::after_stack_pop`] is called by this function automatically. + /// + /// The high-level version of this is `return_from_current_stack_frame`. + /// + /// [`M::before_stack_pop`]: Machine::before_stack_pop + /// [`M::after_stack_pop`]: Machine::after_stack_pop + pub(super) fn pop_stack_frame_raw( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { + let cleanup = self.cleanup_current_frame_locals()?; + + let frame = + self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + + let return_to_block = frame.return_to_block; + let return_place = frame.return_place.clone(); + + let return_action; + if cleanup { + return_action = M::after_stack_pop(self, frame, unwinding)?; + assert_ne!(return_action, ReturnAction::NoCleanup); + } else { + return_action = ReturnAction::NoCleanup; + }; + + Ok(StackPopInfo { return_action, return_to_block, return_place }) + } + + /// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw). + /// Returns `true` if cleanup has been done, `false` otherwise. + fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { + // Cleanup: deallocate locals. + // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. + // We do this while the frame is still on the stack, so errors point to the callee. + let return_to_block = self.frame().return_to_block; + let cleanup = match return_to_block { + StackPopCleanup::Goto { .. } => true, + StackPopCleanup::Root { cleanup, .. } => cleanup, + }; + + if cleanup { + // We need to take the locals out, since we need to mutate while iterating. + let locals = mem::take(&mut self.frame_mut().locals); + for local in &locals { + self.deallocate_local(local.value)?; + } + } + + Ok(cleanup) + } + + /// In the current stack frame, mark all locals as live that are not arguments and don't have + /// `Storage*` annotations (this includes the return place). + pub(crate) fn storage_live_for_always_live_locals(&mut self) -> InterpResult<'tcx> { + self.storage_live(mir::RETURN_PLACE)?; + + let body = self.body(); + let always_live = always_storage_live_locals(body); + for local in body.vars_and_temps_iter() { + if always_live.contains(local) { + self.storage_live(local)?; + } + } + Ok(()) + } + + pub fn storage_live_dyn( + &mut self, + local: mir::Local, + meta: MemPlaceMeta, + ) -> InterpResult<'tcx> { + trace!("{:?} is now live", local); + + // We avoid `ty.is_trivially_sized` since that does something expensive for ADTs. + fn is_very_trivially_sized(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Never + | ty::Error(_) + | ty::Dynamic(_, _, ty::DynStar) => true, + + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, + + ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), + + ty::Pat(ty, ..) => is_very_trivially_sized(*ty), + + // We don't want to do any queries, so there is not much we can do with ADTs. + ty::Adt(..) => false, + + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false, + + ty::Infer(ty::TyVar(_)) => false, + + ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("`is_very_trivially_sized` applied to unexpected type: {}", ty) + } + } + } + + // This is a hot function, we avoid computing the layout when possible. + // `unsized_` will be `None` for sized types and `Some(layout)` for unsized types. + let unsized_ = if is_very_trivially_sized(self.body().local_decls[local].ty) { + None + } else { + // We need the layout. + let layout = self.layout_of_local(self.frame(), local, None)?; + if layout.is_sized() { None } else { Some(layout) } + }; + + let local_val = LocalValue::Live(if let Some(layout) = unsized_ { + if !meta.has_meta() { + throw_unsup!(UnsizedLocal); + } + // Need to allocate some memory, since `Immediate::Uninit` cannot be unsized. + let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?; + Operand::Indirect(*dest_place.mplace()) + } else { + assert!(!meta.has_meta()); // we're dropping the metadata + // Just make this an efficient immediate. + // Note that not calling `layout_of` here does have one real consequence: + // if the type is too big, we'll only notice this when the local is actually initialized, + // which is a bit too late -- we should ideally notice this already here, when the memory + // is conceptually allocated. But given how rare that error is and that this is a hot function, + // we accept this downside for now. + Operand::Immediate(Immediate::Uninit) + }); + + // If the local is already live, deallocate its old memory. + let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); + self.deallocate_local(old)?; + Ok(()) + } + + /// Mark a storage as live, killing the previous content. + #[inline(always)] + pub fn storage_live(&mut self, local: mir::Local) -> InterpResult<'tcx> { + self.storage_live_dyn(local, MemPlaceMeta::None) + } + + pub fn storage_dead(&mut self, local: mir::Local) -> InterpResult<'tcx> { + assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); + trace!("{:?} is now dead", local); + + // If the local is already dead, this is a NOP. + let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); + self.deallocate_local(old)?; + Ok(()) + } + + fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { + if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { + // All locals have a backing allocation, even if the allocation is empty + // due to the local having ZST type. Hence we can `unwrap`. + trace!( + "deallocating local {:?}: {:?}", + local, + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) + ); + self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; + }; + Ok(()) + } + + #[inline(always)] + pub(super) fn layout_of_local( + &self, + frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, + local: mir::Local, + layout: Option>, + ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { + let state = &frame.locals[local]; + if let Some(layout) = state.layout.get() { + return Ok(layout); + } + + let layout = from_known_layout(self.tcx, self.param_env, layout, || { + let local_ty = frame.body.local_decls[local].ty; + let local_ty = + self.instantiate_from_frame_and_normalize_erasing_regions(frame, local_ty)?; + self.layout_of(local_ty) + })?; + + // Layouts of locals are requested a lot, so we cache them. + state.layout.set(Some(layout)); + Ok(layout) + } +} + +impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { + pub(super) fn print( + &self, + allocs: &mut Vec>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + match self.value { + LocalValue::Dead => write!(fmt, " is dead")?, + LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { + write!(fmt, " is uninitialized")? + } + LocalValue::Live(Operand::Indirect(mplace)) => { + write!( + fmt, + " by {} ref {:?}:", + match mplace.meta { + MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"), + MemPlaceMeta::None => String::new(), + }, + mplace.ptr, + )?; + allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id)); + } + LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { + write!(fmt, " {val:?}")?; + if let Scalar::Ptr(ptr, _size) = val { + allocs.push(ptr.provenance.get_alloc_id()); + } + } + LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { + write!(fmt, " ({val1:?}, {val2:?})")?; + if let Scalar::Ptr(ptr, _size) = val1 { + allocs.push(ptr.provenance.get_alloc_id()); + } + if let Scalar::Ptr(ptr, _size) = val2 { + allocs.push(ptr.provenance.get_alloc_id()); + } + } + } + + Ok(()) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 48433d95c5113..2527eca344620 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -4,16 +4,29 @@ use either::Either; use rustc_index::IndexSlice; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; +use rustc_span::source_map::Spanned; +use rustc_target::abi::call::FnAbi; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use tracing::{info, instrument, trace}; use super::{ - ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar, + throw_ub, FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, + PlaceTy, Projectable, Scalar, }; use crate::util; +struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> { + callee: FnVal<'tcx, M::ExtraFnVal>, + args: Vec>, + fn_sig: ty::FnSig<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`]) + with_caller_location: bool, +} + impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` as long as there are more things to do. /// @@ -37,7 +50,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if let Some(stmt) = basic_block.statements.get(loc.statement_index) { let old_frames = self.frame_idx(); - self.statement(stmt)?; + self.eval_statement(stmt)?; // Make sure we are not updating `statement_index` of the wrong frame. assert_eq!(old_frames, self.frame_idx()); // Advance the program counter. @@ -48,7 +61,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::before_terminator(self)?; let terminator = basic_block.terminator(); - self.terminator(terminator)?; + self.eval_terminator(terminator)?; + if !self.stack().is_empty() { + if let Either::Left(loc) = self.frame().loc { + info!("// executing {:?}", loc.block); + } + } Ok(true) } @@ -56,7 +74,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// statement counter. /// /// This does NOT move the statement counter forward, the caller has to do that! - pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { + pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { info!("{:?}", stmt); use rustc_middle::mir::StatementKind::*; @@ -94,7 +112,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::retag_place_contents(self, *kind, &dest)?; } - Intrinsic(box intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?, + Intrinsic(box intrinsic) => self.eval_nondiverging_intrinsic(intrinsic)?, // Evaluate the place expression, without reading from it. PlaceMention(box place) => { @@ -179,6 +197,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_immediate(*result, &dest)?; } + NullaryOp(null_op, ty) => { + let ty = self.instantiate_from_current_frame_and_normalize_erasing_regions(ty)?; + let val = self.nullary_op(null_op, ty)?; + self.write_immediate(*val, &dest)?; + } + Aggregate(box ref kind, ref operands) => { self.write_aggregate(kind, operands, &dest)?; } @@ -230,38 +254,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_immediate(*val, &dest)?; } - NullaryOp(ref null_op, ty) => { - let ty = self.instantiate_from_current_frame_and_normalize_erasing_regions(ty)?; - let layout = self.layout_of(ty)?; - if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op - && layout.is_unsized() - { - span_bug!( - self.frame().current_span(), - "{null_op:?} MIR operator called for unsized type {ty}", - ); - } - let val = match null_op { - mir::NullOp::SizeOf => { - let val = layout.size.bytes(); - Scalar::from_target_usize(val, self) - } - mir::NullOp::AlignOf => { - let val = layout.align.abi.bytes(); - Scalar::from_target_usize(val, self) - } - mir::NullOp::OffsetOf(fields) => { - let val = self - .tcx - .offset_of_subfield(self.param_env, layout, fields.iter()) - .bytes(); - Scalar::from_target_usize(val, self) - } - mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()), - }; - self.write_scalar(val, &dest)?; - } - ShallowInitBox(ref operand, _) => { let src = self.eval_operand(operand, None)?; let v = self.read_immediate(&src)?; @@ -362,7 +354,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // of the first element. let elem_size = first.layout.size; let first_ptr = first.ptr(); - let rest_ptr = first_ptr.offset(elem_size, self)?; + let rest_ptr = first_ptr.wrapping_offset(elem_size, self); // No alignment requirement since `copy_op` above already checked it. self.mem_copy_repeatedly( first_ptr, @@ -376,16 +368,222 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } - /// Evaluate the given terminator. Will also adjust the stack frame and statement position accordingly. - fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { + /// Evaluate the arguments of a function call + fn eval_fn_call_argument( + &self, + op: &mir::Operand<'tcx>, + ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { + Ok(match op { + mir::Operand::Copy(_) | mir::Operand::Constant(_) => { + // Make a regular copy. + let op = self.eval_operand(op, None)?; + FnArg::Copy(op) + } + mir::Operand::Move(place) => { + // If this place lives in memory, preserve its location. + // We call `place_to_op` which will be an `MPlaceTy` whenever there exists + // an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local` + // which can return a local even if that has an mplace.) + let place = self.eval_place(*place)?; + let op = self.place_to_op(&place)?; + + match op.as_mplace_or_imm() { + Either::Left(mplace) => FnArg::InPlace(mplace), + Either::Right(_imm) => { + // This argument doesn't live in memory, so there's no place + // to make inaccessible during the call. + // We rely on there not being any stray `PlaceTy` that would let the + // caller directly access this local! + // This is also crucial for tail calls, where we want the `FnArg` to + // stay valid when the old stack frame gets popped. + FnArg::Copy(op) + } + } + } + }) + } + + /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the + /// necessary information about callee and arguments to make a call. + fn eval_callee_and_args( + &self, + terminator: &mir::Terminator<'tcx>, + func: &mir::Operand<'tcx>, + args: &[Spanned>], + ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { + let func = self.eval_operand(func, None)?; + let args = args + .iter() + .map(|arg| self.eval_fn_call_argument(&arg.node)) + .collect::>>()?; + + let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); + let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = + self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); + + let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { + ty::FnPtr(_sig) => { + let fn_ptr = self.read_pointer(&func)?; + let fn_val = self.get_ptr_fn(fn_ptr)?; + (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) + } + ty::FnDef(def_id, args) => { + let instance = self.resolve(def_id, args)?; + ( + FnVal::Instance(instance), + self.fn_abi_of_instance(instance, extra_args)?, + instance.def.requires_caller_location(*self.tcx), + ) + } + _ => { + span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty) + } + }; + + Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + } + + fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> { info!("{:?}", terminator.kind); - self.eval_terminator(terminator)?; - if !self.stack().is_empty() { - if let Either::Left(loc) = self.frame().loc { - info!("// executing {:?}", loc.block); + use rustc_middle::mir::TerminatorKind::*; + match terminator.kind { + Return => { + self.return_from_current_stack_frame(/* unwinding */ false)? + } + + Goto { target } => self.go_to_block(target), + + SwitchInt { ref discr, ref targets } => { + let discr = self.read_immediate(&self.eval_operand(discr, None)?)?; + trace!("SwitchInt({:?})", *discr); + + // Branch to the `otherwise` case by default, if no match is found. + let mut target_block = targets.otherwise(); + + for (const_int, target) in targets.iter() { + // Compare using MIR BinOp::Eq, to also support pointer values. + // (Avoiding `self.binary_op` as that does some redundant layout computation.) + let res = self.binary_op( + mir::BinOp::Eq, + &discr, + &ImmTy::from_uint(const_int, discr.layout), + )?; + if res.to_scalar().to_bool()? { + target_block = target; + break; + } + } + + self.go_to_block(target_block); + } + + Call { + ref func, + ref args, + destination, + target, + unwind, + call_source: _, + fn_span: _, + } => { + let old_stack = self.frame_idx(); + let old_loc = self.frame().loc; + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; + + let destination = self.force_allocation(&self.eval_place(destination)?)?; + self.init_fn_call( + callee, + (fn_sig.abi, fn_abi), + &args, + with_caller_location, + &destination, + target, + if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable }, + )?; + // Sanity-check that `eval_fn_call` either pushed a new frame or + // did a jump to another block. + if self.frame_idx() == old_stack && self.frame().loc == old_loc { + span_bug!(terminator.source_info.span, "evaluating this call made no progress"); + } + } + + TailCall { ref func, ref args, fn_span: _ } => { + let old_frame_idx = self.frame_idx(); + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; + + self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; + + if self.frame_idx() != old_frame_idx { + span_bug!( + terminator.source_info.span, + "evaluating this tail call pushed a new stack frame" + ); + } + } + + Drop { place, target, unwind, replace: _ } => { + let place = self.eval_place(place)?; + let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); + if let ty::InstanceKind::DropGlue(_, None) = instance.def { + // This is the branch we enter if and only if the dropped type has no drop glue + // whatsoever. This can happen as a result of monomorphizing a drop of a + // generic. In order to make sure that generic and non-generic code behaves + // roughly the same (and in keeping with Mir semantics) we do nothing here. + self.go_to_block(target); + return Ok(()); + } + trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty); + self.init_drop_in_place_call(&place, instance, target, unwind)?; + } + + Assert { ref cond, expected, ref msg, target, unwind } => { + let ignored = + M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check(); + let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?; + if ignored || expected == cond_val { + self.go_to_block(target); + } else { + M::assert_panic(self, msg, unwind)?; + } + } + + UnwindTerminate(reason) => { + M::unwind_terminate(self, reason)?; + } + + // When we encounter Resume, we've finished unwinding + // cleanup for the current stack frame. We pop it in order + // to continue unwinding the next frame + UnwindResume => { + trace!("unwinding: resuming from cleanup"); + // By definition, a Resume terminator means + // that we're unwinding + self.return_from_current_stack_frame(/* unwinding */ true)?; + return Ok(()); + } + + // It is UB to ever encounter this. + Unreachable => throw_ub!(Unreachable), + + // These should never occur for MIR we actually run. + FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!( + terminator.source_info.span, + "{:#?} should have been eliminated by MIR pass", + terminator.kind + ), + + InlineAsm { template, ref operands, options, ref targets, .. } => { + M::eval_inline_asm(self, template, operands, options, targets)?; } } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index adb6ebabd73af..460f5448634b5 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -155,8 +155,8 @@ impl CtfeValidationMode { /// State for tracking recursive validation of references pub struct RefTracking { - pub seen: FxHashSet, - pub todo: Vec<(T, PATH)>, + seen: FxHashSet, + todo: Vec<(T, PATH)>, } impl RefTracking { @@ -169,8 +169,11 @@ impl RefTracking ref_tracking_for_consts.seen.insert(op); ref_tracking_for_consts } + pub fn next(&mut self) -> Option<(T, PATH)> { + self.todo.pop() + } - pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) { + fn track(&mut self, op: T, path: impl FnOnce() -> PATH) { if self.seen.insert(op.clone()) { trace!("Recursing below ptr {:#?}", op); let path = path(); @@ -435,88 +438,96 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { throw_validation_failure!(self.path, NullPtr { ptr_kind }) } - // Do not allow pointers to uninhabited types. + // Do not allow references to uninhabited types. if place.layout.abi.is_uninhabited() { let ty = place.layout.ty; throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty }) } // Recursive checking if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() { - // Determine whether this pointer expects to be pointing to something mutable. - let ptr_expected_mutbl = match ptr_kind { - PointerKind::Box => Mutability::Mut, - PointerKind::Ref(mutbl) => { - // We do not take into account interior mutability here since we cannot know if - // there really is an `UnsafeCell` inside `Option` -- so we check - // that in the recursive descent behind this reference (controlled by - // `allow_immutable_unsafe_cell`). - mutbl - } - }; // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. - if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { + if let Some(ctfe_mode) = self.ctfe_mode { let mut skip_recursive_check = false; - if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id) + // CTFE imposes restrictions on what references can point to. + if let Ok((alloc_id, _offset, _prov)) = + self.ecx.ptr_try_get_alloc_id(place.ptr(), 0) { - let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() }; - // Special handling for pointers to statics (irrespective of their type). - assert!(!self.ecx.tcx.is_thread_local_static(did)); - assert!(self.ecx.tcx.is_static(did)); - // Mode-specific checks - match self.ctfe_mode { - Some( - CtfeValidationMode::Static { .. } | CtfeValidationMode::Promoted { .. }, - ) => { - // We skip recursively checking other statics. These statics must be sound by - // themselves, and the only way to get broken statics here is by using - // unsafe code. - // The reasons we don't check other statics is twofold. For one, in all - // sound cases, the static was already validated on its own, and second, we - // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us (potentially through a promoted). - // This could miss some UB, but that's fine. - // We still walk nested allocations, as they are fundamentally part of this validation run. - // This means we will also recurse into nested statics of *other* - // statics, even though we do not recurse into other statics directly. - // That's somewhat inconsistent but harmless. - skip_recursive_check = !nested; - } - Some(CtfeValidationMode::Const { .. }) => { - // We can't recursively validate `extern static`, so we better reject them. - if self.ecx.tcx.is_foreign_item(did) { - throw_validation_failure!(self.path, ConstRefToExtern); + if let Some(GlobalAlloc::Static(did)) = + self.ecx.tcx.try_get_global_alloc(alloc_id) + { + let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { + bug!() + }; + // Special handling for pointers to statics (irrespective of their type). + assert!(!self.ecx.tcx.is_thread_local_static(did)); + assert!(self.ecx.tcx.is_static(did)); + // Mode-specific checks + match ctfe_mode { + CtfeValidationMode::Static { .. } + | CtfeValidationMode::Promoted { .. } => { + // We skip recursively checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us (potentially through a promoted). + // This could miss some UB, but that's fine. + // We still walk nested allocations, as they are fundamentally part of this validation run. + // This means we will also recurse into nested statics of *other* + // statics, even though we do not recurse into other statics directly. + // That's somewhat inconsistent but harmless. + skip_recursive_check = !nested; + } + CtfeValidationMode::Const { .. } => { + // We can't recursively validate `extern static`, so we better reject them. + if self.ecx.tcx.is_foreign_item(did) { + throw_validation_failure!(self.path, ConstRefToExtern); + } } } - None => {} } - } - // Dangling and Mutability check. - let (size, _align, alloc_kind) = self.ecx.get_alloc_info(alloc_id); - if alloc_kind == AllocKind::Dead { - // This can happen for zero-sized references. We can't have *any* references to non-existing - // allocations though, interning rejects them all as the rest of rustc isn't happy with them... - // so we throw an error, even though this isn't really UB. - // A potential future alternative would be to resurrect this as a zero-sized allocation - // (which codegen will then compile to an aligned dummy pointer anyway). - throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind }); - } - // If this allocation has size zero, there is no actual mutability here. - if size != Size::ZERO { - let alloc_actual_mutbl = mutability(self.ecx, alloc_id); - // Mutable pointer to immutable memory is no good. - if ptr_expected_mutbl == Mutability::Mut - && alloc_actual_mutbl == Mutability::Not - { - throw_validation_failure!(self.path, MutableRefToImmutable); + // Dangling and Mutability check. + let (size, _align, alloc_kind) = self.ecx.get_alloc_info(alloc_id); + if alloc_kind == AllocKind::Dead { + // This can happen for zero-sized references. We can't have *any* references to + // non-existing allocations in const-eval though, interning rejects them all as + // the rest of rustc isn't happy with them... so we throw an error, even though + // this isn't really UB. + // A potential future alternative would be to resurrect this as a zero-sized allocation + // (which codegen will then compile to an aligned dummy pointer anyway). + throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind }); } - // In a const, everything must be completely immutable. - if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) { + // If this allocation has size zero, there is no actual mutability here. + if size != Size::ZERO { + // Determine whether this pointer expects to be pointing to something mutable. + let ptr_expected_mutbl = match ptr_kind { + PointerKind::Box => Mutability::Mut, + PointerKind::Ref(mutbl) => { + // We do not take into account interior mutability here since we cannot know if + // there really is an `UnsafeCell` inside `Option` -- so we check + // that in the recursive descent behind this reference (controlled by + // `allow_immutable_unsafe_cell`). + mutbl + } + }; + // Determine what it actually points to. + let alloc_actual_mutbl = mutability(self.ecx, alloc_id); + // Mutable pointer to immutable memory is no good. if ptr_expected_mutbl == Mutability::Mut - || alloc_actual_mutbl == Mutability::Mut + && alloc_actual_mutbl == Mutability::Not { - throw_validation_failure!(self.path, ConstRefToMutable); + throw_validation_failure!(self.path, MutableRefToImmutable); + } + // In a const, everything must be completely immutable. + if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) { + if ptr_expected_mutbl == Mutability::Mut + || alloc_actual_mutbl == Mutability::Mut + { + throw_validation_failure!(self.path, ConstRefToMutable); + } } } } @@ -524,6 +535,15 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if skip_recursive_check { return Ok(()); } + } else { + // This is not CTFE, so it's Miri with recursive checking. + // FIXME: we do *not* check behind boxes, since creating a new box first creates it uninitialized + // and then puts the value in there, so briefly we have a box with uninit contents. + // FIXME: should we also skip `UnsafeCell` behind shared references? Currently that is not + // needed since validation reads bypass Stacked Borrows and data race checks. + if matches!(ptr_kind, PointerKind::Box) { + return Ok(()); + } } let path = &self.path; ref_tracking.track(place, || { @@ -1072,11 +1092,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// `op` is assumed to cover valid memory if it is an indirect operand. /// It will error if the bits at the destination do not match the ones described by the layout. #[inline(always)] - pub fn validate_operand(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + pub fn validate_operand( + &self, + op: &OpTy<'tcx, M::Provenance>, + recursive: bool, + ) -> InterpResult<'tcx> { // Note that we *could* actually be in CTFE here with `-Zextra-const-ub-checks`, but it's // still correct to not use `ctfe_mode`: that mode is for validation of the final constant - // value, it rules out things like `UnsafeCell` in awkward places. It also can make checking - // recurse through references which, for now, we don't want here, either. - self.validate_operand_internal(op, vec![], None, None) + // value, it rules out things like `UnsafeCell` in awkward places. + if !recursive { + return self.validate_operand_internal(op, vec![], None, None); + } + // Do a recursive check. + let mut ref_tracking = RefTracking::empty(); + self.validate_operand_internal(op, vec![], Some(&mut ref_tracking), None)?; + while let Some((mplace, path)) = ref_tracking.todo.pop() { + self.validate_operand_internal(&mplace.into(), path, Some(&mut ref_tracking), None)?; + } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index daf57285ebe6d..cbd1fdeea2aa3 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,6 +1,6 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; @@ -30,10 +30,10 @@ pub fn check_validity_requirement<'tcx>( return Ok(!layout.abi.is_uninhabited()); } + let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { - might_permit_raw_init_strict(layout, tcx, kind) + might_permit_raw_init_strict(layout, &layout_cx, kind) } else { - let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; might_permit_raw_init_lax(layout, &layout_cx, kind) } } @@ -42,12 +42,12 @@ pub fn check_validity_requirement<'tcx>( /// details. fn might_permit_raw_init_strict<'tcx>( ty: TyAndLayout<'tcx>, - tcx: TyCtxt<'tcx>, + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, kind: ValidityRequirement, ) -> Result> { let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); - let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); + let mut cx = InterpCx::new(cx.tcx, rustc_span::DUMMY_SP, cx.param_env, machine); let allocated = cx .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) @@ -67,7 +67,7 @@ fn might_permit_raw_init_strict<'tcx>( // This does *not* actually check that references are dereferenceable, but since all types that // require dereferenceability also require non-null, we don't actually get any false negatives // due to this. - Ok(cx.validate_operand(&ot).is_ok()) + Ok(cx.validate_operand(&ot, /*recursive*/ false).is_ok()) } /// Implements the 'lax' (default) version of the `might_permit_raw_init` checks; see that function for diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 4f654eb09015b..403136e78f4eb 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,7 +10,6 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![cfg_attr(not(parallel_compiler), feature(cell_leak))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d9633d69f1d45..627a0ebb4e5e3 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -909,6 +909,15 @@ pub fn version_at_macro_invocation( ) { let verbose = matches.opt_present("verbose"); + let mut version = version; + let mut release = release; + let tmp; + if let Ok(force_version) = std::env::var("RUSTC_OVERRIDE_VERSION_STRING") { + tmp = force_version; + version = &tmp; + release = &tmp; + } + safe_println!("{binary} {version}"); if verbose { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e1dcbf5b2805b..67ca6d50cca4b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -741,6 +741,16 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + #[rustc_lint_diagnostics] + pub fn highlighted_span_note( + &mut self, + span: impl Into, + msg: Vec, + ) -> &mut Self { + self.sub_with_highlights(Level::Note, msg, span.into()); + self + } + /// This is like [`Diag::note()`], but it's only printed once. #[rustc_lint_diagnostics] pub fn note_once(&mut self, msg: impl Into) -> &mut Self { @@ -815,6 +825,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + /// Add a help message attached to this diagnostic with a customizable highlighted message. + #[rustc_lint_diagnostics] + pub fn highlighted_span_help( + &mut self, + span: impl Into, + msg: Vec, + ) -> &mut Self { + self.sub_with_highlights(Level::Help, msg, span.into()); + self + } + /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 73908e5808569..88ed3128164d1 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1346,10 +1346,11 @@ impl HumanEmitter { buffer.append(0, ": ", header_style); label_width += 2; } - for (text, _) in msgs.iter() { + let mut line = 0; + for (text, style) in msgs.iter() { let text = self.translate_message(text, args).map_err(Report::new).unwrap(); // Account for newlines to align output to its label. - for (line, text) in normalize_whitespace(&text).lines().enumerate() { + for text in normalize_whitespace(&text).lines() { buffer.append( line, &format!( @@ -1357,8 +1358,38 @@ impl HumanEmitter { if line == 0 { String::new() } else { " ".repeat(label_width) }, text ), - header_style, + match style { + Style::Highlight => *style, + _ => header_style, + }, ); + line += 1; + } + // We add lines above, but if the last line has no explicit newline (which would + // yield an empty line), then we revert one line up to continue with the next + // styled text chunk on the same line as the last one from the prior one. Otherwise + // every `text` would appear on their own line (because even though they didn't end + // in '\n', they advanced `line` by one). + if line > 0 { + line -= 1; + } + } + if self.short_message { + let labels = msp + .span_labels() + .into_iter() + .filter_map(|label| match label.label { + Some(msg) if label.is_primary => { + let text = self.translate_message(&msg, args).ok()?; + if !text.trim().is_empty() { Some(text.to_string()) } else { None } + } + _ => None, + }) + .collect::>() + .join(", "); + if !labels.is_empty() { + buffer.append(line, ": ", Style::NoStyle); + buffer.append(line, &labels, Style::NoStyle); } } } @@ -1767,7 +1798,10 @@ impl HumanEmitter { debug!(?suggestions); if suggestions.is_empty() { - // Suggestions coming from macros can have malformed spans. This is a heavy handed + // Here we check if there are suggestions that have actual code changes. We sometimes + // suggest the same code that is already there, instead of changing how we produce the + // suggestions and filtering there, we just don't emit the suggestion. + // Suggestions coming from macros can also have malformed spans. This is a heavy handed // approach to avoid ICEs by ignoring the suggestion outright. return Ok(()); } @@ -2046,7 +2080,9 @@ impl HumanEmitter { assert!(underline_start >= 0 && underline_end >= 0); let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { - if let DisplaySuggestion::Underline = show_code_change { + if let DisplaySuggestion::Underline = show_code_change + && is_different(sm, &part.snippet, part.span) + { // If this is a replacement, underline with `~`, if this is an addition // underline with `+`. buffer.putc( @@ -2559,22 +2595,13 @@ fn num_decimal_digits(num: usize) -> usize { // We replace some characters so the CLI output is always consistent and underlines aligned. // Keep the following list in sync with `rustc_span::char_width`. +// ATTENTION: keep lexicografically sorted so that the binary search will work const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ - ('\t', " "), // We do our own tab replacement - ('\u{200D}', ""), // Replace ZWJ with nothing for consistent terminal output of grapheme clusters. - ('\u{202A}', "�"), // The following unicode text flow control characters are inconsistently - ('\u{202B}', "�"), // supported across CLIs and can cause confusion due to the bytes on disk - ('\u{202D}', "�"), // not corresponding to the visible source code, so we replace them always. - ('\u{202E}', "�"), - ('\u{2066}', "�"), - ('\u{2067}', "�"), - ('\u{2068}', "�"), - ('\u{202C}', "�"), - ('\u{2069}', "�"), + // tidy-alphabetical-start // In terminals without Unicode support the following will be garbled, but in *all* terminals // the underlying codepoint will be as well. We could gate this replacement behind a "unicode // support" gate. - ('\u{0000}', "␀"), + ('\0', "␀"), ('\u{0001}', "␁"), ('\u{0002}', "␂"), ('\u{0003}', "␃"), @@ -2583,11 +2610,12 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{0006}', "␆"), ('\u{0007}', "␇"), ('\u{0008}', "␈"), - ('\u{000B}', "␋"), - ('\u{000C}', "␌"), - ('\u{000D}', "␍"), - ('\u{000E}', "␎"), - ('\u{000F}', "␏"), + ('\u{0009}', " "), // We do our own tab replacement + ('\u{000b}', "␋"), + ('\u{000c}', "␌"), + ('\u{000d}', "␍"), + ('\u{000e}', "␎"), + ('\u{000f}', "␏"), ('\u{0010}', "␐"), ('\u{0011}', "␑"), ('\u{0012}', "␒"), @@ -2598,21 +2626,37 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{0017}', "␗"), ('\u{0018}', "␘"), ('\u{0019}', "␙"), - ('\u{001A}', "␚"), - ('\u{001B}', "␛"), - ('\u{001C}', "␜"), - ('\u{001D}', "␝"), - ('\u{001E}', "␞"), - ('\u{001F}', "␟"), - ('\u{007F}', "␡"), + ('\u{001a}', "␚"), + ('\u{001b}', "␛"), + ('\u{001c}', "␜"), + ('\u{001d}', "␝"), + ('\u{001e}', "␞"), + ('\u{001f}', "␟"), + ('\u{007f}', "␡"), + ('\u{200d}', ""), // Replace ZWJ for consistent terminal output of grapheme clusters. + ('\u{202a}', "�"), // The following unicode text flow control characters are inconsistently + ('\u{202b}', "�"), // supported across CLIs and can cause confusion due to the bytes on disk + ('\u{202c}', "�"), // not corresponding to the visible source code, so we replace them always. + ('\u{202d}', "�"), + ('\u{202e}', "�"), + ('\u{2066}', "�"), + ('\u{2067}', "�"), + ('\u{2068}', "�"), + ('\u{2069}', "�"), + // tidy-alphabetical-end ]; -fn normalize_whitespace(str: &str) -> String { - let mut s = str.to_string(); - for (c, replacement) in OUTPUT_REPLACEMENTS { - s = s.replace(*c, replacement); - } - s +fn normalize_whitespace(s: &str) -> String { + // Scan the input string for a character in the ordered table above. If it's present, replace + // it with it's alternative string (it can be more than 1 char!). Otherwise, retain the input + // char. At the end, allocate all chars into a string in one operation. + s.chars().fold(String::with_capacity(s.len()), |mut s, c| { + match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) { + Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1), + _ => s.push(c), + } + s + }) } fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { @@ -2824,6 +2868,18 @@ impl Style { } } +/// Whether the original and suggested code are the same. +pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool { + let found = match sm.span_to_snippet(sp) { + Ok(snippet) => snippet, + Err(e) => { + warn!(error = ?e, "Invalid span {:?}", sp); + return true; + } + }; + found != suggested +} + /// Whether the original and suggested code are visually similar enough to warrant extra wording. pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 09855394cdb2b..ceebcd46a6f7f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -50,7 +50,7 @@ pub use diagnostic_impls::{ IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; -use emitter::{is_case_difference, DynEmitter, Emitter}; +use emitter::{is_case_difference, is_different, DynEmitter, Emitter}; use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; @@ -357,10 +357,16 @@ impl CodeSuggestion { _ => 1, }) .sum(); - line_highlight.push(SubstitutionHighlight { - start: (cur_lo.col.0 as isize + acc) as usize, - end: (cur_lo.col.0 as isize + acc + len) as usize, - }); + if !is_different(sm, &part.snippet, part.span) { + // Account for cases where we are suggesting the same code that's already + // there. This shouldn't happen often, but in some cases for multipart + // suggestions it's much easier to handle it here than in the origin. + } else { + line_highlight.push(SubstitutionHighlight { + start: (cur_lo.col.0 as isize + acc) as usize, + end: (cur_lo.col.0 as isize + acc + len) as usize, + }); + } buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); // Account for the difference between the width of the current code and the @@ -392,7 +398,11 @@ impl CodeSuggestion { while buf.ends_with('\n') { buf.pop(); } - Some((buf, substitution.parts, highlights, only_capitalization)) + if highlights.iter().all(|parts| parts.is_empty()) { + None + } else { + Some((buf, substitution.parts, highlights, only_capitalization)) + } }) .collect() } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 756290be0a813..f6bf9f5e89f2f 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -8,7 +8,9 @@ use rustc_ast::tokenstream::{ use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, NodeId}; use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; -use rustc_feature::{Features, ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES}; +use rustc_feature::{ + AttributeSafety, Features, ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES, +}; use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; use rustc_session::parse::feature_err; @@ -263,6 +265,13 @@ impl<'a> StripUnconfigured<'a> { /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { + validate_attr::check_attribute_safety( + self.features.unwrap_or(&Features::default()), + &self.sess.psess, + AttributeSafety::Normal, + &cfg_attr, + ); + let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(cfg_attr, &self.sess.psess) else { @@ -385,6 +394,13 @@ impl<'a> StripUnconfigured<'a> { return (true, None); } }; + + validate_attr::deny_builtin_meta_unsafety( + self.features.unwrap_or(&Features::default()), + &self.sess.psess, + &meta_item, + ); + ( parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| { attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features) diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 99fcc66a4adc1..e42a655531b5d 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -86,7 +86,7 @@ declare_features! ( /// Allows `c"foo"` literals. (accepted, c_str_literals, "1.77.0", Some(105723)), /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries and treat `extern "C" fn` as nounwind. - (accepted, c_unwind, "CURRENT_RUSTC_VERSION", Some(74990)), + (accepted, c_unwind, "1.81.0", Some(74990)), /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`. (accepted, cfg_attr_multi, "1.33.0", Some(54881)), /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. @@ -238,7 +238,7 @@ declare_features! ( /// Allows `let...else` statements. (accepted, let_else, "1.65.0", Some(87335)), /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check. - (accepted, lint_reasons, "CURRENT_RUSTC_VERSION", Some(54503)), + (accepted, lint_reasons, "1.81.0", Some(54503)), /// Allows `break {expr}` with a value inside `loop`s. (accepted, loop_break_value, "1.19.0", Some(37339)), /// Allows use of `?` as the Kleene "at most one" operator in macros. @@ -390,6 +390,8 @@ declare_features! ( (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208)), /// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block. (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668)), + /// Allows unsafe on extern declarations and safety qualifiers over internal items. + (accepted, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), /// Allows importing and reexporting macros with `use`, /// enables macro modularization in general. (accepted, use_extern_macros, "1.30.0", Some(35896)), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ef03a25bc164b..72ea55d5999a2 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -703,21 +703,21 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, allocator_internals, experimental!(needs_allocator), ), gated!( - panic_runtime, Normal, template!(Word), WarnFollowing, + panic_runtime, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No, experimental!(panic_runtime) ), gated!( - needs_panic_runtime, Normal, template!(Word), WarnFollowing, + needs_panic_runtime, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No, experimental!(needs_panic_runtime) ), gated!( - compiler_builtins, Normal, template!(Word), WarnFollowing, + compiler_builtins, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No, "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ which contains compiler-rt intrinsics and will never be stable", ), gated!( - profiler_runtime, Normal, template!(Word), WarnFollowing, + profiler_runtime, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No, "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \ which contains the profiler runtime and will never be stable", diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 18f9700646a77..b7f0ed5afce93 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -82,6 +82,9 @@ declare_features! ( /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`. (removed, custom_derive, "1.32.0", Some(29644), Some("subsumed by `#[proc_macro_derive]`")), + /// Allows default type parameters to influence type inference. + (removed, default_type_parameter_fallback, "CURRENT_RUSTC_VERSION", Some(27336), + Some("never properly implemented; requires significant design work")), /// Allows using `#[doc(keyword = "...")]`. (removed, doc_keyword, "1.28.0", Some(51315), Some("merged into `#![feature(rustdoc_internals)]`")), @@ -223,7 +226,7 @@ declare_features! ( (removed, unwind_attributes, "1.56.0", Some(58760), Some("use the C-unwind ABI instead")), (removed, visible_private_types, "1.0.0", None, None), /// Allows `extern "wasm" fn` - (removed, wasm_abi, "CURRENT_RUSTC_VERSION", Some(83788), + (removed, wasm_abi, "1.81.0", Some(83788), Some("non-standard wasm ABI is no longer supported")), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a57ff3f7b006f..88a4b5a838246 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -431,8 +431,6 @@ declare_features! ( (unstable, custom_test_frameworks, "1.30.0", Some(50297)), /// Allows declarative macros 2.0 (`macro`). (unstable, decl_macro, "1.17.0", Some(39412)), - /// Allows default type parameters to influence type inference. - (unstable, default_type_parameter_fallback, "1.3.0", Some(27336)), /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. @@ -518,7 +516,7 @@ declare_features! ( /// Give access to additional metadata about declarative macro meta-variables. (unstable, macro_metavar_expr, "1.61.0", Some(83527)), /// Provides a way to concatenate identifiers using metavariable expressions. - (unstable, macro_metavar_expr_concat, "CURRENT_RUSTC_VERSION", Some(124225)), + (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), /// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are @@ -561,11 +559,11 @@ declare_features! ( /// Allows using enums in offset_of! (unstable, offset_of_enum, "1.75.0", Some(120141)), /// Allows using fields with slice type in offset_of! - (unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)), + (unstable, offset_of_slice, "1.81.0", Some(126151)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows specifying nop padding on functions for dynamic patching. - (unstable, patchable_function_entry, "CURRENT_RUSTC_VERSION", Some(123115)), + (unstable, patchable_function_entry, "1.81.0", Some(123115)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "1.79.0", Some(121618)), /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args. @@ -577,7 +575,7 @@ declare_features! ( /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant - (incomplete, ref_pat_eat_one_layer_2024_structural, "CURRENT_RUSTC_VERSION", Some(123076)), + (incomplete, ref_pat_eat_one_layer_2024_structural, "1.81.0", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), /// Allows the `#[repr(i128)]` attribute for enums. @@ -591,6 +589,8 @@ declare_features! ( (incomplete, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics + (unstable, sha512_sm_x86, "CURRENT_RUSTC_VERSION", Some(126624)), /// Shortern the tail expression lifetime (unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)), /// Allows the use of SIMD types in functions declared in `extern` blocks. @@ -629,8 +629,6 @@ declare_features! ( (incomplete, unnamed_fields, "1.74.0", Some(49804)), /// Allows unsafe attributes. (unstable, unsafe_attributes, "1.80.0", Some(123757)), - /// Allows unsafe on extern declarations and safety qualifiers over internal items. - (unstable, unsafe_extern_blocks, "1.80.0", Some(123743)), /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. (incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)), @@ -643,9 +641,9 @@ declare_features! ( /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. (unstable, used_with_arg, "1.60.0", Some(93798)), /// Allows use of x86 `AMX` target-feature attributes and intrinsics - (unstable, x86_amx_intrinsics, "CURRENT_RUSTC_VERSION", Some(126622)), + (unstable, x86_amx_intrinsics, "1.81.0", Some(126622)), /// Allows use of the `xop` target-feature - (unstable, xop_target_feature, "CURRENT_RUSTC_VERSION", Some(127208)), + (unstable, xop_target_feature, "1.81.0", Some(127208)), /// Allows `do yeet` expressions (unstable, yeet_expr, "1.62.0", Some(96373)), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3b9aea087910a..33e8432596be1 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2952,6 +2952,17 @@ pub struct FnDecl<'hir> { pub lifetime_elision_allowed: bool, } +impl<'hir> FnDecl<'hir> { + pub fn opt_delegation_sig_id(&self) -> Option { + if let FnRetTy::Return(ty) = self.output + && let TyKind::InferDelegation(sig_id, _) = ty.kind + { + return Some(sig_id); + } + None + } +} + /// Represents what type of implicit self a function has, if any. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum ImplicitSelfKind { @@ -3689,6 +3700,11 @@ impl<'hir> OwnerNode<'hir> { } } + /// Check if node is an impl block. + pub fn is_impl_block(&self) -> bool { + matches!(self, OwnerNode::Item(Item { kind: ItemKind::Impl(_), .. })) + } + expect_methods_self! { expect_item, &'hir Item<'hir>, OwnerNode::Item(n), n; expect_foreign_item, &'hir ForeignItem<'hir>, OwnerNode::ForeignItem(n), n; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 2e965c59ebb53..847a1e6470679 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -2,7 +2,7 @@ use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; -use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; +use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Symbol; @@ -455,32 +455,22 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ); } } - // No special checking is needed for these: - // - Typeck has checked that Const operands are integers. - // - AST lowering guarantees that SymStatic points to a static. - hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {} - // Check that sym actually points to a function. Later passes - // depend on this. + // Typeck has checked that Const operands are integers. + hir::InlineAsmOperand::Const { anon_const } => { + debug_assert!(matches!( + self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(), + ty::Error(_) | ty::Int(_) | ty::Uint(_) + )); + } + // Typeck has checked that SymFn refers to a function. hir::InlineAsmOperand::SymFn { anon_const } => { - let ty = self.tcx.type_of(anon_const.def_id).instantiate_identity(); - match ty.kind() { - ty::Never | ty::Error(_) => {} - ty::FnDef(..) => {} - _ => { - self.tcx - .dcx() - .struct_span_err(*op_sp, "invalid `sym` operand") - .with_span_label( - self.tcx.def_span(anon_const.def_id), - format!("is {} `{}`", ty.kind().article(), ty), - ) - .with_help( - "`sym` operands must refer to either a function or a static", - ) - .emit(); - } - }; + debug_assert!(matches!( + self.tcx.type_of(anon_const.def_id).instantiate_identity().kind(), + ty::Error(_) | ty::FnDef(..) + )); } + // AST lowering guarantees that SymStatic points to a static. + hir::InlineAsmOperand::SymStatic { .. } => {} // No special checking is needed for labels. hir::InlineAsmOperand::Label { .. } => {} } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 80a65aa298851..60e2c2eb30e56 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -54,13 +54,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { }; } - // For a delegation item inherit generics from callee. - if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id) - && let Some(generics) = inherit_generics_for_delegation_item(tcx, def_id, sig_id) - { - return generics; - } - let hir_id = tcx.local_def_id_to_hir_id(def_id); let node = tcx.hir_node(hir_id); @@ -234,6 +227,16 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // inherit the generics of the item. Some(parent.to_def_id()) } + ItemKind::Fn(sig, _, _) => { + // For a delegation item inherit generics from callee. + if let Some(sig_id) = sig.decl.opt_delegation_sig_id() + && let Some(generics) = + inherit_generics_for_delegation_item(tcx, def_id, sig_id) + { + return generics; + } + None + } _ => None, }, _ => None, @@ -335,8 +338,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { if default.is_some() { match allow_defaults { Defaults::Allowed => {} - Defaults::FutureCompatDisallowed - if tcx.features().default_type_parameter_fallback => {} Defaults::FutureCompatDisallowed => { tcx.node_span_lint( lint::builtin::INVALID_TYPE_PARAM_DEFAULT, diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ae52dbd56f9b7..a5a56cb845d79 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -115,13 +115,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen None => {} } - // For a delegation item inherit predicates from callee. - if let Some(sig_id) = tcx.hir().opt_delegation_sig_id(def_id) - && let Some(predicates) = inherit_predicates_for_delegation_item(tcx, def_id, sig_id) - { - return predicates; - } - let hir_id = tcx.local_def_id_to_hir_id(def_id); let node = tcx.hir_node(hir_id); @@ -151,6 +144,16 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => { is_trait = Some(self_bounds); } + + ItemKind::Fn(sig, _, _) => { + // For a delegation item inherit predicates from callee. + if let Some(sig_id) = sig.decl.opt_delegation_sig_id() + && let Some(predicates) = + inherit_predicates_for_delegation_item(tcx, def_id, sig_id) + { + return predicates; + } + } _ => {} } }; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 592a3cb552644..8cb4ba6c6691d 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -7,7 +7,7 @@ use rustc_hir::HirId; use rustc_middle::query::plumbing::CyclePlaceholder; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; @@ -34,6 +34,20 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { let parent_node_id = tcx.parent_hir_id(hir_id); let parent_node = tcx.hir_node(parent_node_id); + let find_sym_fn = |&(op, op_sp)| match op { + hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == hir_id => { + Some((anon_const, op_sp)) + } + _ => None, + }; + + let find_const = |&(op, op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == hir_id => { + Some((anon_const, op_sp)) + } + _ => None, + }; + match parent_node { // Anon consts "inside" the type system. Node::ConstArg(&ConstArg { @@ -45,13 +59,51 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // Anon consts outside the type system. Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) - if asm.operands.iter().any(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id, - _ => false, - }) => + if let Some((anon_const, op_sp)) = asm.operands.iter().find_map(find_sym_fn) => { - tcx.typeck(def_id).node_type(hir_id) + let ty = tcx.typeck(def_id).node_type(hir_id); + + match ty.kind() { + ty::Error(_) => ty, + ty::FnDef(..) => ty, + _ => { + let guar = tcx + .dcx() + .struct_span_err(op_sp, "invalid `sym` operand") + .with_span_label( + tcx.def_span(anon_const.def_id), + format!("is {} `{}`", ty.kind().article(), ty), + ) + .with_help("`sym` operands must refer to either a function or a static") + .emit(); + + Ty::new_error(tcx, guar) + } + } + } + Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) + | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) + if let Some((anon_const, op_sp)) = asm.operands.iter().find_map(find_const) => + { + let ty = tcx.typeck(def_id).node_type(hir_id); + + match ty.kind() { + ty::Error(_) => ty, + ty::Int(_) | ty::Uint(_) => ty, + _ => { + let guar = tcx + .dcx() + .struct_span_err(op_sp, "invalid type for `const` operand") + .with_span_label( + tcx.def_span(anon_const.def_id), + format!("is {} `{}`", ty.kind().article(), ty), + ) + .with_help("`const` operands must be of an integer type") + .emit(); + + Ty::new_error(tcx, guar) + } + } } Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx) diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index e21ed55bce3fc..ca62ef92b83de 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -242,7 +242,7 @@ pub(crate) fn inherit_sig_for_delegation_item<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> &'tcx [Ty<'tcx>] { - let sig_id = tcx.hir().delegation_sig_id(def_id); + let sig_id = tcx.hir().opt_delegation_sig_id(def_id).unwrap(); let caller_sig = tcx.fn_sig(sig_id); if let Err(err) = check_constraints(tcx, def_id, sig_id) { let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index f18224c39ae49..034a4918b505f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -552,21 +552,34 @@ pub(crate) fn check_generic_arg_count( synth_provided, } } else { - let num_missing_args = expected_max - provided; + // Check if associated type bounds are incorrectly written in impl block header like: + // ``` + // trait Foo {} + // impl Foo for u8 {} + // ``` + let parent_is_impl_block = cx + .tcx() + .hir() + .parent_owner_iter(seg.hir_id) + .next() + .is_some_and(|(_, owner_node)| owner_node.is_impl_block()); + if parent_is_impl_block { + let constraint_names: Vec<_> = + gen_args.constraints.iter().map(|b| b.ident.name).collect(); + let param_names: Vec<_> = gen_params + .own_params + .iter() + .filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter + .map(|param| param.name) + .collect(); + if constraint_names == param_names { + // We set this to true and delay emitting `WrongNumberOfGenericArgs` + // to provide a succinct error for cases like issue #113073 + all_params_are_binded = true; + }; + } - let constraint_names: Vec<_> = - gen_args.constraints.iter().map(|b| b.ident.name).collect(); - let param_names: Vec<_> = gen_params - .own_params - .iter() - .filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter - .map(|param| param.name) - .collect(); - if constraint_names == param_names { - // We set this to true and delay emitting `WrongNumberOfGenericArgs` - // to provide a succinct error for cases like issue #113073 - all_params_are_binded = true; - }; + let num_missing_args = expected_max - provided; GenericArgsInfo::MissingTypesOrConsts { num_missing_args, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d75a5f8806bc9..e54f9486f6a89 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1306,6 +1306,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // No way to know whether it's diverging because // of a `break` or an outer `break` or `return`. self.diverges.set(Diverges::Maybe); + } else { + self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); } // If we permit break with a value, then result type is diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index dea125bb9b1dd..8c1aa66332fc9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -48,30 +48,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { - // FIXME: Combine these two 'if' expressions into one once - // let chains are implemented - if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { - // If span arose from a desugaring of `if` or `while`, then it is the condition itself, - // which diverges, that we are about to lint on. This gives suboptimal diagnostics. - // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. - if !span.is_desugaring(DesugaringKind::CondTemporary) - && !span.is_desugaring(DesugaringKind::Async) - && !orig_span.is_desugaring(DesugaringKind::Await) - { - self.diverges.set(Diverges::WarnedAlways); + let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else { + return; + }; - debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + match span.desugaring_kind() { + // If span arose from a desugaring of `if` or `while`, then it is the condition + // itself, which diverges, that we are about to lint on. This gives suboptimal + // diagnostics. Instead, stop here so that the `if`- or `while`-expression's + // block is linted instead. + Some(DesugaringKind::CondTemporary) => return, - let msg = format!("unreachable {kind}"); - self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - lint.primary_message(msg.clone()); - lint.span_label(span, msg).span_label( - orig_span, - custom_note.unwrap_or("any code following this expression is unreachable"), - ); - }) - } + // Don't lint if the result of an async block or async function is `!`. + // This does not affect the unreachable lints *within* the body. + Some(DesugaringKind::Async) => return, + + // Don't lint *within* the `.await` operator, since that's all just desugaring + // junk. We only want to lint if there is a subsequent expression after the + // `.await` operator. + Some(DesugaringKind::Await) => return, + + _ => {} } + + // Don't warn twice. + self.diverges.set(Diverges::WarnedAlways); + + debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + + let msg = format!("unreachable {kind}"); + self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + lint.primary_message(msg.clone()); + lint.span_label(span, msg).span_label( + orig_span, + custom_note.unwrap_or("any code following this expression is unreachable"), + ); + }) } /// Resolves type and const variables in `ty` if possible. Unlike the infcx diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index fa78b9ced1282..758a1cefe6341 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -265,11 +265,10 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), span, .. }) | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), span, .. }) => { asm.operands.iter().find_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => { + hir::InlineAsmOperand::Const { anon_const } + | hir::InlineAsmOperand::SymFn { anon_const } + if anon_const.hir_id == id => + { Some(fcx.next_ty_var(span)) } _ => None, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 82f732d69dc52..28f537c87c4ee 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -774,18 +774,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // instantiation that replaces `Self` with the object type itself. Hence, // a `&self` method will wind up with an argument type like `&dyn Trait`. let trait_ref = principal.with_self_ty(self.tcx, self_ty); - self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| { - this.push_candidate( - Candidate { item, kind: ObjectCandidate(new_trait_ref), import_ids: smallvec![] }, - true, - ); - }); + self.assemble_candidates_for_bounds( + traits::supertraits(self.tcx, trait_ref), + |this, new_trait_ref, item| { + this.push_candidate( + Candidate { + item, + kind: ObjectCandidate(new_trait_ref), + import_ids: smallvec![], + }, + true, + ); + }, + ); } #[instrument(level = "debug", skip(self))] fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { - // FIXME: do we want to commit to this behavior for param bounds? - let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { @@ -806,7 +811,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } }); - self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { + self.assemble_candidates_for_bounds(bounds, |this, poly_trait_ref, item| { this.push_candidate( Candidate { item, @@ -820,15 +825,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Do a search through a list of bounds, using a callback to actually // create the candidates. - fn elaborate_bounds( + fn assemble_candidates_for_bounds( &mut self, bounds: impl Iterator>, mut mk_cand: F, ) where F: for<'b> FnMut(&mut ProbeContext<'b, 'tcx>, ty::PolyTraitRef<'tcx>, ty::AssocItem), { - let tcx = self.tcx; - for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { + for bound_trait_ref in bounds { debug!("elaborate_bounds(bound_trait_ref={:?})", bound_trait_ref); for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { if !self.has_applicable_self(&item) { diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 886b043af2460..04e2b7d45dc93 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -15,6 +15,7 @@ use rustc_middle::ty; use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; use rustc_parse::new_parser_from_source_str; +use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; @@ -67,7 +68,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { } match new_parser_from_source_str(&psess, filename, s.to_string()) { - Ok(mut parser) => match parser.parse_meta_item() { + Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => { if meta_item.path.segments.len() != 1 { error!("argument key must be an identifier"); @@ -173,7 +174,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch } }; - let meta_item = match parser.parse_meta_item() { + let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::Yes) { Ok(meta_item) if parser.token == token::Eof => meta_item, Ok(..) => expected_error(), Err(err) => { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a5e25b917dc5b..8c99b1f444766 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -544,7 +544,13 @@ fn resolver_for_lowering_raw<'tcx>( let arenas = Resolver::arenas(); let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal(); - let mut resolver = Resolver::new(tcx, &pre_configured_attrs, krate.spans.inner_span, &arenas); + let mut resolver = Resolver::new( + tcx, + &pre_configured_attrs, + krate.spans.inner_span, + krate.spans.inject_use_span, + &arenas, + ); let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver); // Make sure we don't mutate the cstore from here on. diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 987dbf6db630a..d0be6cbee6e19 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -518,8 +518,8 @@ lint_non_binding_let_multi_suggestion = lint_non_binding_let_on_drop_type = non-binding let on a type that implements `Drop` -lint_non_binding_let_on_sync_lock = - non-binding let on a synchronization lock +lint_non_binding_let_on_sync_lock = non-binding let on a synchronization lock + .label = this lock is not assigned to a binding and is immediately dropped lint_non_binding_let_suggestion = consider binding to an unused variable to avoid immediately dropping the value @@ -700,10 +700,10 @@ lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last lint_redundant_import = the item `{$ident}` is imported redundantly - .label_imported_here = the item `{ident}` is already imported here - .label_defined_here = the item `{ident}` is already defined here - .label_imported_prelude = the item `{ident}` is already imported by the extern prelude - .label_defined_prelude = the item `{ident}` is already defined by the extern prelude + .label_imported_here = the item `{$ident}` is already imported here + .label_defined_here = the item `{$ident}` is already defined here + .label_imported_prelude = the item `{$ident}` is already imported by the extern prelude + .label_defined_prelude = the item `{$ident}` is already defined by the extern prelude lint_redundant_import_visibility = glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough .note = the most public imported item is `{$max_vis}` diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 92db8a88e42d6..1368cc87e3e16 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -104,7 +104,6 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; @@ -156,12 +155,12 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)), }; if is_sync_lock { - let mut span = MultiSpan::from_span(pat.span); - span.push_span_label( - pat.span, - "this lock is not assigned to a binding and is immediately dropped".to_string(), + let span = MultiSpan::from_span(pat.span); + cx.emit_span_lint( + LET_UNDERSCORE_LOCK, + span, + NonBindingLet::SyncLock { sub, pat: pat.span }, ); - cx.emit_span_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub }); // Only emit let_underscore_drop for top-level `_` patterns. } else if can_use_init.is_some() { cx.emit_span_lint(LET_UNDERSCORE_DROP, local.span, NonBindingLet::DropType { sub }); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 196b8fd52d53f..4f3933d461bbd 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -543,7 +543,7 @@ fn register_builtins(store: &mut LintStore) { ); store.register_removed( "suspicious_auto_trait_impls", - "no longer needed, see #93367 \ + "no longer needed, see issue #93367 \ for more information", ); store.register_removed( @@ -565,6 +565,11 @@ fn register_builtins(store: &mut LintStore) { "box_pointers", "it does not detect other kinds of allocations, and existed only for historical reasons", ); + store.register_removed( + "byte_slice_in_packed_struct_with_derive", + "converted into hard error, see issue #107457 \ + for more information", + ) } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 46e7655a656b3..1a657d31865a1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -957,6 +957,8 @@ pub struct BadOptAccessDiag<'a> { pub enum NonBindingLet { #[diag(lint_non_binding_let_on_sync_lock)] SyncLock { + #[label] + pat: Span, #[subdiagnostic] sub: NonBindingLetSub, }, diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs index c39c86f6fe8f1..e3b1967da09c7 100644 --- a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs +++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs @@ -1,5 +1,5 @@ -//! Migration code for the `expr_fragment_specifier_2024` -//! rule. +//! Migration code for the `expr_fragment_specifier_2024` rule. + use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_session::lint::FutureIncompatibilityReason; diff --git a/compiler/rustc_lint/src/precedence.rs b/compiler/rustc_lint/src/precedence.rs index eb2ba39727797..52321e25c7d43 100644 --- a/compiler/rustc_lint/src/precedence.rs +++ b/compiler/rustc_lint/src/precedence.rs @@ -16,6 +16,7 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail + /// # #![deny(ambiguous_negative_literals)] /// # #![allow(unused)] /// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1 /// ``` @@ -27,7 +28,7 @@ declare_lint! { /// Method calls take precedence over unary precedence. Setting the /// precedence explicitly makes the code clearer and avoid potential bugs. pub AMBIGUOUS_NEGATIVE_LITERALS, - Deny, + Allow, "ambiguous negative literals operations", report_in_external_macro } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index e9f44f3af0272..5b17c0d718a44 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -309,11 +309,7 @@ fn report_bin_hex_error( ) { let (t, actually) = match ty { attr::IntType::SignedInt(t) => { - let actually = if negative { - -(size.sign_extend(val) as i128) - } else { - size.sign_extend(val) as i128 - }; + let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; (t.name_str(), actually.to_string()) } attr::IntType::UnsignedInt(t) => { @@ -462,8 +458,11 @@ fn lint_int_literal<'tcx>( } let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; - let lit = - cx.sess().source_map().span_to_snippet(span).expect("must get snippet from literal"); + let lit = cx + .sess() + .source_map() + .span_to_snippet(span) + .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); @@ -489,6 +488,7 @@ fn lint_uint_literal<'tcx>( ast::LitKind::Int(v, _) => v.get(), _ => bug!(), }; + if lit_val < min || lit_val > max { if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { match par_e.kind { @@ -530,7 +530,7 @@ fn lint_uint_literal<'tcx>( .sess() .source_map() .span_to_snippet(lit.span) - .expect("must get snippet from literal"), + .unwrap_or_else(|_| lit_val.to_string()), min, max, }, @@ -555,14 +555,14 @@ fn lint_literal<'tcx>( } ty::Uint(t) => lint_uint_literal(cx, e, lit, t), ty::Float(t) => { - let is_infinite = match lit.node { + let (is_infinite, sym) = match lit.node { ast::LitKind::Float(v, _) => match t { // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI // issues resolved). - ty::FloatTy::F16 => Ok(false), - ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), - ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), - ty::FloatTy::F128 => Ok(false), + ty::FloatTy::F16 => (Ok(false), v), + ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), + ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), + ty::FloatTy::F128 => (Ok(false), v), }, _ => bug!(), }; @@ -576,7 +576,7 @@ fn lint_literal<'tcx>( .sess() .source_map() .span_to_snippet(lit.span) - .expect("must get snippet from literal"), + .unwrap_or_else(|_| sym.to_string()), }, ); } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 246b516076461..ff0bdfcc9d261 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -26,7 +26,6 @@ declare_lint_pass! { BARE_TRAIT_OBJECTS, BINDINGS_WITH_VARIANT_NAME, BREAK_WITH_LABEL_AND_LOOP, - BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, CENUM_IMPL_DROP_CAST, COHERENCE_LEAK_CHECK, CONFLICTING_REPR_HINTS, @@ -82,6 +81,7 @@ declare_lint_pass! { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PTR_CAST_ADD_AUTO_TO_OBJECT, PUB_USE_OF_PRIVATE_EXTERN_CRATE, + REDUNDANT_IMPORTS, REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, @@ -426,6 +426,31 @@ declare_lint! { "imports that are never used" } +declare_lint! { + /// The `redundant_imports` lint detects imports that are redundant due to being + /// imported already; either through a previous import, or being present in + /// the prelude. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(redundant_imports)] + /// use std::option::Option::None; + /// fn foo() -> Option { None } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Redundant imports are unnecessary and can be removed to simplify code. + /// If you intended to re-export the item to make it available outside of the + /// module, add a visibility modifier like `pub`. + pub REDUNDANT_IMPORTS, + Allow, + "imports that are redundant due to being imported already" +} + declare_lint! { /// The `must_not_suspend` lint guards against values that shouldn't be held across suspend points /// (`.await`) @@ -617,8 +642,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![cfg_attr(bootstrap, feature(lint_reasons))] - /// /// #[expect(unused_variables)] /// let x = 10; /// println!("{}", x); @@ -1243,7 +1266,7 @@ declare_lint! { Deny, "type parameter default erroneously allowed in invalid location", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, reference: "issue #36887 ", }; } @@ -1836,8 +1859,7 @@ declare_lint! { /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions pub ELIDED_LIFETIMES_IN_PATHS, Allow, - "hidden lifetime parameters in types are deprecated", - crate_level_only + "hidden lifetime parameters in types are deprecated" } declare_lint! { @@ -4292,39 +4314,6 @@ declare_lint! { report_in_external_macro } -declare_lint! { - /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field - /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or - /// more built-in traits. - /// - /// ### Example - /// - /// ```rust - /// #[repr(packed)] - /// #[derive(Hash)] - /// struct FlexZeroSlice { - /// width: u8, - /// data: [u8], - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This was previously accepted but is being phased out, because fields in packed structs are - /// now required to implement `Copy` for `derive` to work. Byte slices and string slices are a - /// temporary exception because certain crates depended on them. - pub BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, - Warn, - "`[u8]` or `str` used in a packed struct with `derive`", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #107457 ", - }; - report_in_external_macro -} - declare_lint! { /// The `invalid_macro_export_arguments` lint detects cases where `#[macro_export]` is being used with invalid arguments. /// @@ -4911,7 +4900,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(unsafe_extern_blocks)] /// #![warn(missing_unsafe_on_extern)] /// #![allow(dead_code)] /// diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 4cdd8af1008c0..2ff7335a0fc81 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1555,7 +1555,7 @@ LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) { extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_GE(18, 0) +#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) return wrap(llvm::Intrinsic::getDeclaration( unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update)); #else diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 1705c016437cc..4c243e6330b91 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -747,18 +747,7 @@ impl<'hir> Map<'hir> { } pub fn opt_delegation_sig_id(self, def_id: LocalDefId) -> Option { - if let Some(ret) = self.get_fn_output(def_id) - && let FnRetTy::Return(ty) = ret - && let TyKind::InferDelegation(sig_id, _) = ty.kind - { - return Some(sig_id); - } - None - } - - #[inline] - pub fn delegation_sig_id(self, def_id: LocalDefId) -> DefId { - self.opt_delegation_sig_id(def_id).unwrap() + self.tcx.opt_hir_owner_node(def_id)?.fn_decl()?.opt_delegation_sig_id() } #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index d2d91333ffe33..69ce3e087350b 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -334,14 +334,15 @@ pub enum UndefinedBehaviorInfo<'tcx> { alloc_size: Size, ptr_offset: i64, /// The size of the memory range that was expected to be in-bounds. - inbounds_size: Size, + inbounds_size: i64, msg: CheckInAllocMsg, }, /// Using an integer as a pointer in the wrong way. DanglingIntPointer { addr: u64, - /// The size of the memory range that was expected to be in-bounds (or 0 if we don't know). - inbounds_size: Size, + /// The size of the memory range that was expected to be in-bounds (or 0 if we need an + /// allocation but not any actual memory there, e.g. for function pointers). + inbounds_size: i64, msg: CheckInAllocMsg, }, /// Used a pointer with bad alignment. diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 42f30c14cea88..6cfd07d699c3e 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -5,7 +5,7 @@ use rustc_data_structures::static_assert_size; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{HasDataLayout, Size}; -use super::{AllocId, InterpResult}; +use super::AllocId; //////////////////////////////////////////////////////////////////////////////// // Pointer arithmetic @@ -40,62 +40,13 @@ pub trait PointerArithmetic: HasDataLayout { } #[inline] - fn target_usize_to_isize(&self, val: u64) -> i64 { - let val = val as i64; - // Now wrap-around into the machine_isize range. - if val > self.target_isize_max() { - // This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into - // i64. - debug_assert!(self.pointer_size().bits() < 64); - let max_usize_plus_1 = 1u128 << self.pointer_size().bits(); - val - i64::try_from(max_usize_plus_1).unwrap() - } else { - val - } - } - - /// Helper function: truncate given value-"overflowed flag" pair to pointer size and - /// update "overflowed flag" if there was an overflow. - /// This should be called by all the other methods before returning! - #[inline] - fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) { - let val = u128::from(val); - let max_ptr_plus_1 = 1u128 << self.pointer_size().bits(); - (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1) - } - - #[inline] - fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { - // We do not need to check if i fits in a machine usize. If it doesn't, - // either the wrapping_add will wrap or res will not fit in a pointer. - let res = val.overflowing_add(i); - self.truncate_to_ptr(res) - } - - #[inline] - fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { - // We need to make sure that i fits in a machine isize. - let n = i.unsigned_abs(); - if i >= 0 { - let (val, over) = self.overflowing_offset(val, n); - (val, over || i > self.target_isize_max()) - } else { - let res = val.overflowing_sub(n); - let (val, over) = self.truncate_to_ptr(res); - (val, over || i < self.target_isize_min()) - } - } - - #[inline] - fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> { - let (res, over) = self.overflowing_offset(val, i); - if over { throw_ub!(PointerArithOverflow) } else { Ok(res) } + fn truncate_to_target_usize(&self, val: u64) -> u64 { + self.pointer_size().truncate(val.into()).try_into().unwrap() } #[inline] - fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> { - let (res, over) = self.overflowing_signed_offset(val, i); - if over { throw_ub!(PointerArithOverflow) } else { Ok(res) } + fn sign_extend_to_target_isize(&self, val: u64) -> i64 { + self.pointer_size().sign_extend(val.into()).try_into().unwrap() } } @@ -181,12 +132,9 @@ impl Provenance for CtfeProvenance { fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Print AllocId. fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag - // Print offset only if it is non-zero. Print it signed. - let signed_offset = ptr.offset.bytes() as i64; - if signed_offset > 0 { - write!(f, "+{:#x}", signed_offset)?; - } else if signed_offset < 0 { - write!(f, "-{:#x}", signed_offset.unsigned_abs())?; + // Print offset only if it is non-zero. + if ptr.offset.bytes() > 0 { + write!(f, "+{:#x}", ptr.offset.bytes())?; } // Print immutable status. if ptr.provenance.immutable() { @@ -334,7 +282,7 @@ impl Pointer> { } } -impl<'tcx, Prov> Pointer { +impl Pointer { #[inline(always)] pub fn new(provenance: Prov, offset: Size) -> Self { Pointer { provenance, offset } @@ -352,43 +300,16 @@ impl<'tcx, Prov> Pointer { Pointer { provenance: f(self.provenance), ..self } } - #[inline] - pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - Ok(Pointer { - offset: Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?), - ..self - }) - } - - #[inline] - pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) { - let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes()); - let ptr = Pointer { offset: Size::from_bytes(res), ..self }; - (ptr, over) - } - #[inline(always)] pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self { - self.overflowing_offset(i, cx).0 - } - - #[inline] - pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - Ok(Pointer { - offset: Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?), - ..self - }) - } - - #[inline] - pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) { - let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i); - let ptr = Pointer { offset: Size::from_bytes(res), ..self }; - (ptr, over) + let res = + cx.data_layout().truncate_to_target_usize(self.offset.bytes().wrapping_add(i.bytes())); + Pointer { offset: Size::from_bytes(res), ..self } } #[inline(always)] pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { - self.overflowing_signed_offset(i, cx).0 + // It's wrapping anyway, so we can just cast to `u64`. + self.wrapping_offset(Size::from_bytes(i as u64), cx) } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 491d7cbcfe09c..84c17b39a623e 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -393,7 +393,7 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_int(self, size: Size) -> InterpResult<'tcx, i128> { let b = self.to_bits(size)?; - Ok(size.sign_extend(b) as i128) + Ok(size.sign_extend(b)) } /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index b9c93edcd80e4..46c4d586f6ad9 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -383,15 +383,17 @@ pub struct Body<'tcx> { /// Constants that are required to evaluate successfully for this MIR to be well-formed. /// We hold in this field all the constants we are not able to evaluate yet. + /// `None` indicates that the list has not been computed yet. /// /// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a /// function have successfully evaluated if the function ever gets executed at runtime. - pub required_consts: Vec>, + pub required_consts: Option>>, /// Further items that were mentioned in this function and hence *may* become monomorphized, /// depending on optimizations. We use this to avoid optimization-dependent compile errors: the /// collector recursively traverses all "mentioned" items and evaluates all their /// `required_consts`. + /// `None` indicates that the list has not been computed yet. /// /// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee. /// All that's relevant is that this set is optimization-level-independent, and that it includes @@ -399,7 +401,7 @@ pub struct Body<'tcx> { /// set after drop elaboration, so some drop calls that can never be reached are not considered /// "mentioned".) See the documentation of `CollectionMode` in /// `compiler/rustc_monomorphize/src/collector.rs` for more context. - pub mentioned_items: Vec>>, + pub mentioned_items: Option>>>, /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. /// @@ -477,8 +479,8 @@ impl<'tcx> Body<'tcx> { spread_arg: None, var_debug_info, span, - required_consts: Vec::new(), - mentioned_items: Vec::new(), + required_consts: None, + mentioned_items: None, is_polymorphic: false, injection_phase: None, tainted_by_errors, @@ -507,8 +509,8 @@ impl<'tcx> Body<'tcx> { arg_count: 0, spread_arg: None, span: DUMMY_SP, - required_consts: Vec::new(), - mentioned_items: Vec::new(), + required_consts: None, + mentioned_items: None, var_debug_info: Vec::new(), is_polymorphic: false, injection_phase: None, @@ -785,6 +787,40 @@ impl<'tcx> Body<'tcx> { // No inlined `SourceScope`s, or all of them were `#[track_caller]`. caller_location.unwrap_or_else(|| from_span(source_info.span)) } + + #[track_caller] + pub fn set_required_consts(&mut self, required_consts: Vec>) { + assert!( + self.required_consts.is_none(), + "required_consts for {:?} have already been set", + self.source.def_id() + ); + self.required_consts = Some(required_consts); + } + #[track_caller] + pub fn required_consts(&self) -> &[ConstOperand<'tcx>] { + match &self.required_consts { + Some(l) => l, + None => panic!("required_consts for {:?} have not yet been set", self.source.def_id()), + } + } + + #[track_caller] + pub fn set_mentioned_items(&mut self, mentioned_items: Vec>>) { + assert!( + self.mentioned_items.is_none(), + "mentioned_items for {:?} have already been set", + self.source.def_id() + ); + self.mentioned_items = Some(mentioned_items); + } + #[track_caller] + pub fn mentioned_items(&self) -> &[Spanned>] { + match &self.mentioned_items { + Some(l) => l, + None => panic!("mentioned_items for {:?} have not yet been set", self.source.def_id()), + } + } } impl<'tcx> Index for Body<'tcx> { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 0031ded244062..3921176873c81 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1066,9 +1066,11 @@ macro_rules! super_body { $self.visit_span($(& $mutability)? $body.span); - for const_ in &$($mutability)? $body.required_consts { - let location = Location::START; - $self.visit_const_operand(const_, location); + if let Some(required_consts) = &$($mutability)? $body.required_consts { + for const_ in required_consts { + let location = Location::START; + $self.visit_const_operand(const_, location); + } } } } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 690c0121b2b36..f2ea32275f9b1 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::{ TyCtxt, UpvarArgs, }; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, ErrorGuaranteed, Span, Symbol, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_target::asm::InlineAsmRegOrRegClass; use tracing::instrument; @@ -597,10 +597,6 @@ pub struct Pat<'tcx> { } impl<'tcx> Pat<'tcx> { - pub fn wildcard_from_ty(ty: Ty<'tcx>) -> Self { - Pat { ty, span: DUMMY_SP, kind: PatKind::Wild } - } - pub fn simple_ident(&self) -> Option { match self.kind { PatKind::Binding { @@ -1073,186 +1069,6 @@ impl<'tcx> PatRangeBoundary<'tcx> { } } -impl<'tcx> Pat<'tcx> { - /// Prints a [`Pat`] to an owned string, for user-facing diagnostics. - /// - /// If we ever switch over to storing subpatterns as `PatId`, this will also - /// need to take a context that can resolve IDs to subpatterns. - pub fn to_string(&self) -> String { - format!("{}", self.display()) - } - - /// Used internally by [`fmt::Display`] for [`PatDisplay`]. - fn display(&self) -> PatDisplay<'_, 'tcx> { - PatDisplay { pat: self } - } -} - -/// Wrapper around [`&Pat<'tcx>`][`Pat`] that implements [`fmt::Display`]. -/// -/// If we ever switch over to storing subpatterns as `PatId`, this will also -/// need to hold a context that can resolve IDs to subpatterns. -struct PatDisplay<'pat, 'tcx> { - pat: &'pat Pat<'tcx>, -} - -impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let &Self { pat } = self; - - // Printing lists is a chore. - let mut first = true; - let mut start_or_continue = |s| { - if first { - first = false; - "" - } else { - s - } - }; - let mut start_or_comma = || start_or_continue(", "); - - match pat.kind { - PatKind::Wild => write!(f, "_"), - PatKind::Never => write!(f, "!"), - PatKind::AscribeUserType { ref subpattern, .. } => { - write!(f, "{}: _", subpattern.display()) - } - PatKind::Binding { name, mode, ref subpattern, .. } => { - f.write_str(mode.prefix_str())?; - write!(f, "{name}")?; - if let Some(ref subpattern) = *subpattern { - write!(f, " @ {}", subpattern.display())?; - } - Ok(()) - } - PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { - let variant_and_name = match pat.kind { - PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| { - let variant = adt_def.variant(variant_index); - let adt_did = adt_def.did(); - let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did) - || tcx.get_diagnostic_item(sym::Result) == Some(adt_did) - { - variant.name.to_string() - } else { - format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) - }; - Some((variant, name)) - }), - _ => pat.ty.ty_adt_def().and_then(|adt_def| { - if !adt_def.is_enum() { - ty::tls::with(|tcx| { - Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did()))) - }) - } else { - None - } - }), - }; - - if let Some((variant, name)) = &variant_and_name { - write!(f, "{name}")?; - - // Only for Adt we can have `S {...}`, - // which we handle separately here. - if variant.ctor.is_none() { - write!(f, " {{ ")?; - - let mut printed = 0; - for p in subpatterns { - if let PatKind::Wild = p.pattern.kind { - continue; - } - let name = variant.fields[p.field].name; - write!(f, "{}{}: {}", start_or_comma(), name, p.pattern.display())?; - printed += 1; - } - - let is_union = pat.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); - if printed < variant.fields.len() && (!is_union || printed == 0) { - write!(f, "{}..", start_or_comma())?; - } - - return write!(f, " }}"); - } - } - - let num_fields = - variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); - if num_fields != 0 || variant_and_name.is_none() { - write!(f, "(")?; - for i in 0..num_fields { - write!(f, "{}", start_or_comma())?; - - // Common case: the field is where we expect it. - if let Some(p) = subpatterns.get(i) { - if p.field.index() == i { - write!(f, "{}", p.pattern.display())?; - continue; - } - } - - // Otherwise, we have to go looking for it. - if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { - write!(f, "{}", p.pattern.display())?; - } else { - write!(f, "_")?; - } - } - write!(f, ")")?; - } - - Ok(()) - } - PatKind::Deref { ref subpattern } => { - match pat.ty.kind() { - ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, - ty::Ref(_, _, mutbl) => { - write!(f, "&{}", mutbl.prefix_str())?; - } - _ => bug!("{} is a bad Deref pattern type", pat.ty), - } - write!(f, "{}", subpattern.display()) - } - PatKind::DerefPattern { ref subpattern, .. } => { - write!(f, "deref!({})", subpattern.display()) - } - PatKind::Constant { value } => write!(f, "{value}"), - PatKind::InlineConstant { def: _, ref subpattern } => { - write!(f, "{} (from inline const)", subpattern.display()) - } - PatKind::Range(ref range) => write!(f, "{range}"), - PatKind::Slice { ref prefix, ref slice, ref suffix } - | PatKind::Array { ref prefix, ref slice, ref suffix } => { - write!(f, "[")?; - for p in prefix.iter() { - write!(f, "{}{}", start_or_comma(), p.display())?; - } - if let Some(ref slice) = *slice { - write!(f, "{}", start_or_comma())?; - match slice.kind { - PatKind::Wild => {} - _ => write!(f, "{}", slice.display())?, - } - write!(f, "..")?; - } - for p in suffix.iter() { - write!(f, "{}{}", start_or_comma(), p.display())?; - } - write!(f, "]") - } - PatKind::Or { ref pats } => { - for pat in pats.iter() { - write!(f, "{}{}", start_or_continue(" | "), pat.display())?; - } - Ok(()) - } - PatKind::Error(_) => write!(f, ""), - } - } -} - // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 6bfdb3d973607..0024a2ae756ea 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -234,7 +234,7 @@ impl ScalarInt { let data = i.into(); // `into` performed sign extension, we have to truncate let r = Self::raw(size.truncate(data as u128), size); - (r, size.sign_extend(r.data) as i128 != data) + (r, size.sign_extend(r.data) != data) } #[inline] @@ -335,7 +335,7 @@ impl ScalarInt { #[inline] pub fn to_int(self, size: Size) -> i128 { let b = self.to_bits(size); - size.sign_extend(b) as i128 + size.sign_extend(b) } /// Converts the `ScalarInt` to i8. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 8b6c9a4a10d65..3cf8531bb62d5 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -79,7 +79,7 @@ impl<'tcx> Discr<'tcx> { let (val, oflo) = if signed { let min = size.signed_int_min(); let max = size.signed_int_max(); - let val = size.sign_extend(self.val) as i128; + let val = size.sign_extend(self.val); assert!(n < (i128::MAX as u128)); let n = n as i128; let oflo = val > max - n; diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index 38acb6f44f95c..28477e527c721 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -54,8 +54,8 @@ pub(super) fn build_custom_mir<'tcx>( spread_arg: None, var_debug_info: Vec::new(), span, - required_consts: Vec::new(), - mentioned_items: Vec::new(), + required_consts: None, + mentioned_items: None, is_polymorphic: false, tainted_by_errors: None, injection_phase: None, diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index 3d4b706aa652b..56896d945e5f3 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -75,6 +75,9 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { @call(mir_call, args) => { self.parse_call(args) }, + @call(mir_tail_call, args) => { + self.parse_tail_call(args) + }, ExprKind::Match { scrutinee, arms, .. } => { let discr = self.parse_operand(*scrutinee)?; self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t }) @@ -187,6 +190,25 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ) } + fn parse_tail_call(&self, args: &[ExprId]) -> PResult> { + parse_by_kind!(self, args[0], _, "tail call", + ExprKind::Call { fun, args, fn_span, .. } => { + let fun = self.parse_operand(*fun)?; + let args = args + .iter() + .map(|arg| + Ok(Spanned { node: self.parse_operand(*arg)?, span: self.thir.exprs[*arg].span } ) + ) + .collect::>>()?; + Ok(TerminatorKind::TailCall { + func: fun, + args, + fn_span: *fn_span, + }) + }, + ) + } + fn parse_rvalue(&self, expr_id: ExprId) -> PResult> { parse_by_kind!(self, expr_id, expr, "rvalue", @call(mir_discriminant, args) => self.parse_place(args[0]).map(Rvalue::Discriminant), diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 6c34978a29c96..cae4aa7bad3d2 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -17,7 +17,6 @@ use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; use rustc_target::abi::VariantIdx; use tracing::{debug, instrument}; -use util::visit_bindings; use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; @@ -366,28 +365,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span)); - let mut arm_candidates = self.create_match_candidates(&scrutinee_place, arms); - - let match_has_guard = arm_candidates.iter().any(|(_, candidate)| candidate.has_guard); - let mut candidates = - arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::>(); - + let arms = arms.iter().map(|arm| &self.thir[*arm]); let match_start_span = span.shrink_to_lo().to(scrutinee_span); - - // The set of places that we are creating fake borrows of. If there are no match guards then - // we don't need any fake borrows, so don't track them. - let fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard { - util::collect_fake_borrows(self, &candidates, scrutinee_span, scrutinee_place.base()) - } else { - Vec::new() - }; - - self.lower_match_tree( + let patterns = arms + .clone() + .map(|arm| { + let has_match_guard = + if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No }; + (&*arm.pattern, has_match_guard) + }) + .collect(); + let built_tree = self.lower_match_tree( block, scrutinee_span, &scrutinee_place, match_start_span, - &mut candidates, + patterns, false, ); @@ -395,9 +388,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination, scrutinee_place, scrutinee_span, - arm_candidates, + arms, + built_tree, self.source_info(span), - fake_borrow_temps, ) } @@ -417,51 +410,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(scrutinee_place_builder) } - /// Create the initial `Candidate`s for a `match` expression. - fn create_match_candidates<'pat>( - &mut self, - scrutinee: &PlaceBuilder<'tcx>, - arms: &'pat [ArmId], - ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> - where - 'a: 'pat, - { - // Assemble the initial list of candidates. These top-level candidates - // are 1:1 with the original match arms, but other parts of match - // lowering also introduce subcandidates (for subpatterns), and will - // also flatten candidates in some cases. So in general a list of - // candidates does _not_ necessarily correspond to a list of arms. - arms.iter() - .copied() - .map(|arm| { - let arm = &self.thir[arm]; - let arm_has_guard = arm.guard.is_some(); - let arm_candidate = - Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard, self); - (arm, arm_candidate) - }) - .collect() - } - /// Lower the bindings, guards and arm bodies of a `match` expression. /// /// The decision tree should have already been created /// (by [Builder::lower_match_tree]). /// /// `outer_source_info` is the SourceInfo for the whole match. - fn lower_match_arms( + fn lower_match_arms<'pat>( &mut self, destination: Place<'tcx>, scrutinee_place_builder: PlaceBuilder<'tcx>, scrutinee_span: Span, - arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, + arms: impl IntoIterator>, + built_match_tree: BuiltMatchTree<'tcx>, outer_source_info: SourceInfo, - fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>, - ) -> BlockAnd<()> { - let arm_end_blocks: Vec = arm_candidates + ) -> BlockAnd<()> + where + 'tcx: 'pat, + { + let arm_end_blocks: Vec = arms .into_iter() - .map(|(arm, candidate)| { - debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate); + .zip(built_match_tree.branches) + .map(|(arm, branch)| { + debug!("lowering arm {:?}\ncorresponding branch = {:?}", arm, branch); let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); @@ -494,8 +465,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_block = this.bind_pattern( outer_source_info, - candidate, - &fake_borrow_temps, + branch, + &built_match_tree.fake_borrow_temps, scrutinee_span, Some((arm, match_scope)), EmitStorageLive::Yes, @@ -548,18 +519,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_pattern( &mut self, outer_source_info: SourceInfo, - candidate: Candidate<'_, 'tcx>, + branch: MatchTreeBranch<'tcx>, fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, emit_storage_live: EmitStorageLive, ) -> BasicBlock { - if candidate.subcandidates.is_empty() { - // Avoid generating another `BasicBlock` when we only have one - // candidate. + if branch.sub_branches.len() == 1 { + let [sub_branch] = branch.sub_branches.try_into().unwrap(); + // Avoid generating another `BasicBlock` when we only have one sub branch. self.bind_and_guard_matched_candidate( - candidate, - &[], + sub_branch, fake_borrow_temps, scrutinee_span, arm_match_scope, @@ -587,35 +557,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // We keep a stack of all of the bindings and type ascriptions // from the parent candidates that we visit, that also need to // be bound for each candidate. - traverse_candidate( - candidate, - &mut Vec::new(), - &mut |leaf_candidate, parent_data| { - if let Some(arm) = arm { - self.clear_top_scope(arm.scope); - } - let binding_end = self.bind_and_guard_matched_candidate( - leaf_candidate, - parent_data, - fake_borrow_temps, - scrutinee_span, - arm_match_scope, - schedule_drops, - emit_storage_live, - ); - if arm.is_none() { - schedule_drops = ScheduleDrops::No; - } - self.cfg.goto(binding_end, outer_source_info, target_block); - }, - |inner_candidate, parent_data| { - parent_data.push(inner_candidate.extra_data); - inner_candidate.subcandidates.into_iter() - }, - |parent_data| { - parent_data.pop(); - }, - ); + for sub_branch in branch.sub_branches { + if let Some(arm) = arm { + self.clear_top_scope(arm.scope); + } + let binding_end = self.bind_and_guard_matched_candidate( + sub_branch, + fake_borrow_temps, + scrutinee_span, + arm_match_scope, + schedule_drops, + emit_storage_live, + ); + if arm.is_none() { + schedule_drops = ScheduleDrops::No; + } + self.cfg.goto(binding_end, outer_source_info, target_block); + } target_block } @@ -725,7 +683,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { initializer: PlaceBuilder<'tcx>, set_match_place: bool, ) -> BlockAnd<()> { - let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self); + let built_tree = self.lower_match_tree( + block, + irrefutable_pat.span, + &initializer, + irrefutable_pat.span, + vec![(irrefutable_pat, HasMatchGuard::No)], + false, + ); + let [branch] = built_tree.branches.try_into().unwrap(); // For matches and function arguments, the place that is being matched // can be set when creating the variables. But the place for @@ -746,7 +712,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // }; // ``` if let Some(place) = initializer.try_to_place(self) { - visit_bindings(&[&mut candidate], |binding: &Binding<'_>| { + // Because or-alternatives bind the same variables, we only explore the first one. + let first_sub_branch = branch.sub_branches.first().unwrap(); + for binding in &first_sub_branch.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); if let LocalInfo::User(BindingForm::Var(VarBindingForm { opt_match_place: Some((ref mut match_place, _)), @@ -757,21 +725,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { bug!("Let binding to non-user variable.") }; - }); + } } } - self.lower_match_tree( - block, - irrefutable_pat.span, - &initializer, - irrefutable_pat.span, - &mut [&mut candidate], - false, - ); self.bind_pattern( self.source_info(irrefutable_pat.span), - candidate, + branch, &[], irrefutable_pat.span, None, @@ -1152,20 +1112,21 @@ struct Candidate<'pat, 'tcx> { /// The earliest block that has only candidates >= this one as descendents. Used for false /// edges, see the doc for [`Builder::match_expr`]. false_edge_start_block: Option, - /// The `false_edge_start_block` of the next candidate. - next_candidate_start_block: Option, } impl<'tcx, 'pat> Candidate<'pat, 'tcx> { fn new( place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, - has_guard: bool, + has_guard: HasMatchGuard, cx: &mut Builder<'_, 'tcx>, ) -> Self { // Use `FlatPat` to build simplified match pairs, then immediately // incorporate them into a new candidate. - Self::from_flat_pat(FlatPat::new(place, pattern, cx), has_guard) + Self::from_flat_pat( + FlatPat::new(place, pattern, cx), + matches!(has_guard, HasMatchGuard::Yes), + ) } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. @@ -1179,7 +1140,6 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { otherwise_block: None, pre_binding_block: None, false_edge_start_block: None, - next_candidate_start_block: None, } } @@ -1199,6 +1159,17 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { |_| {}, ); } + + /// Visit the leaf candidates in reverse order. + fn visit_leaves_rev<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) { + traverse_candidate( + self, + &mut (), + &mut move |c, _| visit_leaf(c), + move |c, _| c.subcandidates.iter_mut().rev(), + |_| {}, + ); + } } /// A depth-first traversal of the `Candidate` and all of its recursive @@ -1409,12 +1380,114 @@ pub(crate) struct ArmHasGuard(pub(crate) bool); /////////////////////////////////////////////////////////////////////////// // Main matching algorithm +/// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will +/// branch to `success_block` when the matched value matches the corresponding pattern. If there is +/// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns. +#[derive(Debug)] +struct MatchTreeSubBranch<'tcx> { + span: Span, + /// The block that is branched to if the corresponding subpattern matches. + success_block: BasicBlock, + /// The block to branch to if this arm had a guard and the guard fails. + otherwise_block: BasicBlock, + /// The bindings to set up in this sub-branch. + bindings: Vec>, + /// The ascriptions to set up in this sub-branch. + ascriptions: Vec>, + /// Whether the sub-branch corresponds to a never pattern. + is_never: bool, +} + +/// A branch in the output of match lowering. +#[derive(Debug)] +struct MatchTreeBranch<'tcx> { + sub_branches: Vec>, +} + +/// The result of generating MIR for a pattern-matching expression. Each input branch/arm/pattern +/// gives rise to an output `MatchTreeBranch`. If one of the patterns matches, we branch to the +/// corresponding `success_block`. If none of the patterns matches, we branch to `otherwise_block`. +/// +/// Each branch is made of one of more sub-branches, corresponding to or-patterns. E.g. +/// ```ignore(illustrative) +/// match foo { +/// (x, false) | (false, x) => {} +/// (true, true) => {} +/// } +/// ``` +/// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each +/// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a +/// different place. +#[derive(Debug)] +struct BuiltMatchTree<'tcx> { + branches: Vec>, + otherwise_block: BasicBlock, + /// If any of the branches had a guard, we collect here the places and locals to fakely borrow + /// to ensure match guards can't modify the values as we match them. For more details, see + /// [`util::collect_fake_borrows`]. + fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>, +} + +impl<'tcx> MatchTreeSubBranch<'tcx> { + fn from_sub_candidate( + candidate: Candidate<'_, 'tcx>, + parent_data: &Vec>, + ) -> Self { + debug_assert!(candidate.match_pairs.is_empty()); + MatchTreeSubBranch { + span: candidate.extra_data.span, + success_block: candidate.pre_binding_block.unwrap(), + otherwise_block: candidate.otherwise_block.unwrap(), + bindings: parent_data + .iter() + .flat_map(|d| &d.bindings) + .chain(&candidate.extra_data.bindings) + .cloned() + .collect(), + ascriptions: parent_data + .iter() + .flat_map(|d| &d.ascriptions) + .cloned() + .chain(candidate.extra_data.ascriptions) + .collect(), + is_never: candidate.extra_data.is_never, + } + } +} + +impl<'tcx> MatchTreeBranch<'tcx> { + fn from_candidate(candidate: Candidate<'_, 'tcx>) -> Self { + let mut sub_branches = Vec::new(); + traverse_candidate( + candidate, + &mut Vec::new(), + &mut |candidate: Candidate<'_, '_>, parent_data: &mut Vec>| { + sub_branches.push(MatchTreeSubBranch::from_sub_candidate(candidate, parent_data)); + }, + |inner_candidate, parent_data| { + parent_data.push(inner_candidate.extra_data); + inner_candidate.subcandidates.into_iter() + }, + |parent_data| { + parent_data.pop(); + }, + ); + MatchTreeBranch { sub_branches } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum HasMatchGuard { + Yes, + No, +} + impl<'a, 'tcx> Builder<'a, 'tcx> { /// The entrypoint of the matching algorithm. Create the decision tree for the match expression, /// starting from `block`. /// - /// Modifies `candidates` to store the bindings and type ascriptions for - /// that candidate. + /// `patterns` is a list of patterns, one for each arm. The associated boolean indicates whether + /// the arm has a guard. /// /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`) /// or not (for `let` and `match`). In the refutable case we return the block to which we branch @@ -1425,31 +1498,76 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span: Span, scrutinee_place_builder: &PlaceBuilder<'tcx>, match_start_span: Span, - candidates: &mut [&mut Candidate<'pat, 'tcx>], + patterns: Vec<(&'pat Pat<'tcx>, HasMatchGuard)>, refutable: bool, - ) -> BasicBlock { + ) -> BuiltMatchTree<'tcx> + where + 'tcx: 'pat, + { + // Assemble the initial list of candidates. These top-level candidates are 1:1 with the + // input patterns, but other parts of match lowering also introduce subcandidates (for + // sub-or-patterns). So inside the algorithm, the candidates list may not correspond to + // match arms directly. + let mut candidates: Vec> = patterns + .into_iter() + .map(|(pat, has_guard)| { + Candidate::new(scrutinee_place_builder.clone(), pat, has_guard, self) + }) + .collect(); + + let fake_borrow_temps = util::collect_fake_borrows( + self, + &candidates, + scrutinee_span, + scrutinee_place_builder.base(), + ); + // This will generate code to test scrutinee_place and branch to the appropriate arm block. - // See the doc comment on `match_candidates` for why we have an otherwise block. + // If none of the arms match, we branch to `otherwise_block`. When lowering a `match` + // expression, exhaustiveness checking ensures that this block is unreachable. + let mut candidate_refs = candidates.iter_mut().collect::>(); let otherwise_block = - self.match_candidates(match_start_span, scrutinee_span, block, candidates); - - // Link each leaf candidate to the `false_edge_start_block` of the next one. - let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; - for candidate in candidates { - candidate.visit_leaves(|leaf_candidate| { - if let Some(ref mut prev) = previous_candidate { - assert!(leaf_candidate.false_edge_start_block.is_some()); - prev.next_candidate_start_block = leaf_candidate.false_edge_start_block; + self.match_candidates(match_start_span, scrutinee_span, block, &mut candidate_refs); + + // Set up false edges so that the borrow-checker cannot make use of the specific CFG we + // generated. We falsely branch from each candidate to the one below it to make it as if we + // were testing match branches one by one in order. In the refutable case we also want a + // false edge to the final failure block. + let mut next_candidate_start_block = if refutable { Some(otherwise_block) } else { None }; + for candidate in candidates.iter_mut().rev() { + let has_guard = candidate.has_guard; + candidate.visit_leaves_rev(|leaf_candidate| { + if let Some(next_candidate_start_block) = next_candidate_start_block { + let source_info = self.source_info(leaf_candidate.extra_data.span); + // Falsely branch to `next_candidate_start_block` before reaching pre_binding. + let old_pre_binding = leaf_candidate.pre_binding_block.unwrap(); + let new_pre_binding = self.cfg.start_new_block(); + self.false_edges( + old_pre_binding, + new_pre_binding, + next_candidate_start_block, + source_info, + ); + leaf_candidate.pre_binding_block = Some(new_pre_binding); + if has_guard { + // Falsely branch to `next_candidate_start_block` also if the guard fails. + let new_otherwise = self.cfg.start_new_block(); + let old_otherwise = leaf_candidate.otherwise_block.unwrap(); + self.false_edges( + new_otherwise, + old_otherwise, + next_candidate_start_block, + source_info, + ); + leaf_candidate.otherwise_block = Some(new_otherwise); + } } - previous_candidate = Some(leaf_candidate); + assert!(leaf_candidate.false_edge_start_block.is_some()); + next_candidate_start_block = leaf_candidate.false_edge_start_block; }); } - if refutable { - // In refutable cases there's always at least one candidate, and we want a false edge to - // the failure block. - previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block) - } else { + if !refutable { // Match checking ensures `otherwise_block` is actually unreachable in irrefutable // cases. let source_info = self.source_info(scrutinee_span); @@ -1479,7 +1597,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); } - otherwise_block + BuiltMatchTree { + branches: candidates.into_iter().map(MatchTreeBranch::from_candidate).collect(), + otherwise_block, + fake_borrow_temps, + } } /// The main match algorithm. It begins with a set of candidates `candidates` and has the job of @@ -2229,17 +2351,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> BlockAnd<()> { let expr_span = self.thir[expr_id].span; let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span)); - let mut candidate = Candidate::new(scrutinee.clone(), pat, false, self); - let otherwise_block = self.lower_match_tree( + let built_tree = self.lower_match_tree( block, expr_span, &scrutinee, pat.span, - &mut [&mut candidate], + vec![(pat, HasMatchGuard::No)], true, ); + let [branch] = built_tree.branches.try_into().unwrap(); - self.break_for_else(otherwise_block, self.source_info(expr_span)); + self.break_for_else(built_tree.otherwise_block, self.source_info(expr_span)); match declare_let_bindings { DeclareLetBindings::Yes => { @@ -2261,7 +2383,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success = self.bind_pattern( self.source_info(pat.span), - candidate, + branch, &[], expr_span, None, @@ -2269,7 +2391,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); // If branch coverage is enabled, record this branch. - self.visit_coverage_conditional_let(pat, success, otherwise_block); + self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block); success.unit() } @@ -2282,52 +2404,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Note: we do not check earlier that if there is a guard, /// there cannot be move bindings. We avoid a use-after-move by only /// moving the binding once the guard has evaluated to true (see below). - fn bind_and_guard_matched_candidate<'pat>( + fn bind_and_guard_matched_candidate( &mut self, - candidate: Candidate<'pat, 'tcx>, - parent_data: &[PatternExtraData<'tcx>], + sub_branch: MatchTreeSubBranch<'tcx>, fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, schedule_drops: ScheduleDrops, emit_storage_live: EmitStorageLive, ) -> BasicBlock { - debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); + debug!("bind_and_guard_matched_candidate(subbranch={:?})", sub_branch); - debug_assert!(candidate.match_pairs.is_empty()); - - let candidate_source_info = self.source_info(candidate.extra_data.span); - - let mut block = candidate.pre_binding_block.unwrap(); - - if candidate.next_candidate_start_block.is_some() { - let fresh_block = self.cfg.start_new_block(); - self.false_edges( - block, - fresh_block, - candidate.next_candidate_start_block, - candidate_source_info, - ); - block = fresh_block; - } + let block = sub_branch.success_block; - if candidate.extra_data.is_never { + if sub_branch.is_never { // This arm has a dummy body, we don't need to generate code for it. `block` is already // unreachable (except via false edge). - let source_info = self.source_info(candidate.extra_data.span); + let source_info = self.source_info(sub_branch.span); self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); return self.cfg.start_new_block(); } - let ascriptions = parent_data - .iter() - .flat_map(|d| &d.ascriptions) - .cloned() - .chain(candidate.extra_data.ascriptions); - let bindings = - parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings); - - self.ascribe_types(block, ascriptions); + self.ascribe_types(block, sub_branch.ascriptions); // Lower an instance of the arm guard (if present) for this candidate, // and then perform bindings for the arm body. @@ -2338,9 +2436,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. - self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); + self.bind_matched_candidate_for_guard( + block, + schedule_drops, + sub_branch.bindings.iter(), + ); let guard_frame = GuardFrame { - locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(), + locals: sub_branch + .bindings + .iter() + .map(|b| GuardFrameLocal::new(b.var_id)) + .collect(), }; debug!("entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); @@ -2376,17 +2482,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp)); } - let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| { - let unreachable = self.cfg.start_new_block(); - self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); - unreachable - }); - self.false_edges( - otherwise_post_guard_block, - otherwise_block, - candidate.next_candidate_start_block, - source_info, - ); + self.cfg.goto(otherwise_post_guard_block, source_info, sub_branch.otherwise_block); // We want to ensure that the matched candidates are bound // after we have confirmed this candidate *and* any @@ -2414,8 +2510,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` // // and that is clearly not correct. - let by_value_bindings = - bindings.filter(|binding| matches!(binding.binding_mode.0, ByRef::No)); + let by_value_bindings = sub_branch + .bindings + .iter() + .filter(|binding| matches!(binding.binding_mode.0, ByRef::No)); // Read all of the by reference bindings to ensure that the // place they refer to can't be modified by the guard. for binding in by_value_bindings.clone() { @@ -2443,7 +2541,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.bind_matched_candidate_for_arm_body( block, schedule_drops, - bindings, + sub_branch.bindings.iter(), emit_storage_live, ); block diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index c80204c4ad171..8491b5fe380c7 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use rustc_data_structures::fx::FxIndexMap; use rustc_middle::mir::*; use rustc_middle::ty::Ty; @@ -18,18 +16,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, from_block: BasicBlock, real_target: BasicBlock, - imaginary_target: Option, + imaginary_target: BasicBlock, source_info: SourceInfo, ) { - match imaginary_target { - Some(target) if target != real_target => { - self.cfg.terminate( - from_block, - source_info, - TerminatorKind::FalseEdge { real_target, imaginary_target: target }, - ); - } - _ => self.cfg.goto(from_block, source_info, real_target), + if imaginary_target != real_target { + self.cfg.terminate( + from_block, + source_info, + TerminatorKind::FalseEdge { real_target, imaginary_target }, + ); + } else { + self.cfg.goto(from_block, source_info, real_target) } } } @@ -71,10 +68,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// a MIR pass run after borrow checking. pub(super) fn collect_fake_borrows<'tcx>( cx: &mut Builder<'_, 'tcx>, - candidates: &[&mut Candidate<'_, 'tcx>], + candidates: &[Candidate<'_, 'tcx>], temp_span: Span, scrutinee_base: PlaceBase, ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { + if candidates.iter().all(|candidate| !candidate.has_guard) { + // Fake borrows are only used when there is a guard. + return Vec::new(); + } let mut collector = FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() }; for candidate in candidates.iter() { @@ -222,57 +223,6 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } -/// Visit all the bindings of these candidates. Because or-alternatives bind the same variables, we -/// only explore the first one of each or-pattern. -pub(super) fn visit_bindings<'tcx>( - candidates: &[&mut Candidate<'_, 'tcx>], - f: impl FnMut(&Binding<'tcx>), -) { - let mut visitor = BindingsVisitor { f, phantom: PhantomData }; - for candidate in candidates.iter() { - visitor.visit_candidate(candidate); - } -} - -pub(super) struct BindingsVisitor<'tcx, F> { - f: F, - phantom: PhantomData<&'tcx ()>, -} - -impl<'tcx, F> BindingsVisitor<'tcx, F> -where - F: FnMut(&Binding<'tcx>), -{ - fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) { - for binding in &candidate.extra_data.bindings { - (self.f)(binding) - } - for match_pair in &candidate.match_pairs { - self.visit_match_pair(match_pair); - } - } - - fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) { - for binding in &flat_pat.extra_data.bindings { - (self.f)(binding) - } - for match_pair in &flat_pat.match_pairs { - self.visit_match_pair(match_pair); - } - } - - fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) { - if let TestCase::Or { pats, .. } = &match_pair.test_case { - // All the or-alternatives should bind the same locals, so we only visit the first one. - self.visit_flat_pat(&pats[0]) - } else { - for subpair in &match_pair.subpairs { - self.visit_match_pair(subpair); - } - } - } -} - #[must_use] pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { match ref_mutability { diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index f864a13a31bbc..07ca51a67aefb 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -25,6 +25,7 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 336aa1fd43f06..e16911d79c378 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -128,7 +128,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Clone dominators as we need them while mutating the body. let dominators = body.basic_blocks.dominators().clone(); - let mut state = VnState::new(tcx, param_env, &ssa, &dominators, &body.local_decls); + let mut state = VnState::new(tcx, body, param_env, &ssa, &dominators, &body.local_decls); ssa.for_each_assignment_mut( body.basic_blocks.as_mut_preserves_cfg(), |local, value, location| { @@ -204,6 +204,7 @@ enum Value<'tcx> { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. + // `disambiguator` is 0 iff the constant is deterministic. disambiguator: usize, }, /// An aggregate value, either tuple/closure/struct/enum. @@ -266,21 +267,29 @@ struct VnState<'body, 'tcx> { impl<'body, 'tcx> VnState<'body, 'tcx> { fn new( tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, param_env: ty::ParamEnv<'tcx>, ssa: &'body SsaLocals, dominators: &'body Dominators, local_decls: &'body LocalDecls<'tcx>, ) -> Self { + // Compute a rough estimate of the number of values in the body from the number of + // statements. This is meant to reduce the number of allocations, but it's all right if + // we miss the exact amount. We estimate based on 2 values per statement (one in LHS and + // one in RHS) and 4 values per terminator (for call operands). + let num_values = + 2 * body.basic_blocks.iter().map(|bbdata| bbdata.statements.len()).sum::() + + 4 * body.basic_blocks.len(); VnState { tcx, ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine), param_env, local_decls, locals: IndexVec::from_elem(None, local_decls), - rev_locals: IndexVec::default(), - values: FxIndexSet::default(), - evaluated: IndexVec::new(), - next_opaque: Some(0), + rev_locals: IndexVec::with_capacity(num_values), + values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), + evaluated: IndexVec::with_capacity(num_values), + next_opaque: Some(1), feature_unsized_locals: tcx.features().unsized_locals, ssa, dominators, @@ -293,9 +302,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let (index, new) = self.values.insert_full(value); let index = VnIndex::from_usize(index); if new { + // Grow `evaluated` and `rev_locals` here to amortize the allocations. let evaluated = self.eval_to_const(index); let _index = self.evaluated.push(evaluated); debug_assert_eq!(index, _index); + // No need to push to `rev_locals` if we finished listing assignments. + if self.next_opaque.is_some() { + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + } } index } @@ -332,7 +347,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let is_sized = !self.feature_unsized_locals || self.local_decls[local].ty.is_sized(self.tcx, self.param_env); if is_sized { - self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local); + self.rev_locals[value].push(local); } } @@ -346,6 +361,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let next_opaque = self.next_opaque.as_mut()?; let disambiguator = *next_opaque; *next_opaque += 1; + // `disambiguator: 0` means deterministic. + debug_assert_ne!(disambiguator, 0); disambiguator }; Some(self.insert(Value::Constant { value, disambiguator })) @@ -353,12 +370,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. - self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 }) + let value = Const::from_bool(self.tcx, flag); + debug_assert!(value.is_deterministic()); + self.insert(Value::Constant { value, disambiguator: 0 }) } fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex { - self.insert_constant(Const::from_scalar(self.tcx, scalar, ty)) - .expect("scalars are deterministic") + // Scalars are deterministic. + let value = Const::from_scalar(self.tcx, scalar, ty); + debug_assert!(value.is_deterministic()); + self.insert(Value::Constant { value, disambiguator: 0 }) } fn insert_tuple(&mut self, values: Vec) -> VnIndex { @@ -671,7 +692,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_place_projection(&mut self, place: &mut Place<'tcx>, location: Location) { // If the projection is indirect, we treat the local as a value, so can replace it with // another local. - if place.is_indirect() + if place.is_indirect_first_projection() && let Some(base) = self.locals[place.local] && let Some(new_local) = self.try_as_local(base, location) && place.local != new_local @@ -773,10 +794,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { location: Location, ) -> Option { match *operand { - Operand::Constant(ref mut constant) => { - let const_ = constant.const_.normalize(self.tcx, self.param_env); - self.insert_constant(const_) - } + Operand::Constant(ref constant) => self.insert_constant(constant.const_), Operand::Copy(ref mut place) | Operand::Move(ref mut place) => { let value = self.simplify_place_value(place, location)?; if let Some(const_) = self.try_as_constant(value) { @@ -1371,8 +1389,13 @@ fn op_to_prop_const<'tcx>( // If this constant has scalar ABI, return it as a `ConstValue::Scalar`. if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi && let Ok(scalar) = ecx.read_scalar(op) - && scalar.try_to_scalar_int().is_ok() { + if !scalar.try_to_scalar_int().is_ok() { + // Check that we do not leak a pointer. + // Those pointers may lose part of their identity in codegen. + // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. + return None; + } return Some(ConstValue::Scalar(scalar)); } @@ -1436,12 +1459,11 @@ impl<'tcx> VnState<'_, 'tcx> { /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR. fn try_as_constant(&mut self, index: VnIndex) -> Option> { - // This was already constant in MIR, do not change it. - if let Value::Constant { value, disambiguator: _ } = *self.get(index) - // If the constant is not deterministic, adding an additional mention of it in MIR will - // not give the same value as the former mention. - && value.is_deterministic() - { + // This was already constant in MIR, do not change it. If the constant is not + // deterministic, adding an additional mention of it in MIR will not give the same value as + // the former mention. + if let Value::Constant { value, disambiguator: 0 } = *self.get(index) { + debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 36b2b3b7c4466..f30732e6aaf3b 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -744,8 +744,8 @@ impl<'tcx> Inliner<'tcx> { // Copy required constants from the callee_body into the caller_body. Although we are only // pushing unevaluated consts to `required_consts`, here they may have been evaluated // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. - caller_body.required_consts.extend( - callee_body.required_consts.into_iter().filter(|ct| ct.const_.is_required_const()), + caller_body.required_consts.as_mut().unwrap().extend( + callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), ); // Now that we incorporated the callee's `required_consts`, we can remove the callee from // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does @@ -755,12 +755,11 @@ impl<'tcx> Inliner<'tcx> { // We need to reconstruct the `required_item` for the callee so that we can find and // remove it. let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); - if let Some(idx) = - caller_body.mentioned_items.iter().position(|item| item.node == callee_item) - { + let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap(); + if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) { // We found the callee, so remove it and add its items instead. - caller_body.mentioned_items.remove(idx); - caller_body.mentioned_items.extend(callee_body.mentioned_items); + caller_mentioned_items.remove(idx); + caller_mentioned_items.extend(callee_body.mentioned_items()); } else { // If we can't find the callee, there's no point in adding its items. Probably it // already got removed by being inlined elsewhere in the same function, so we already diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index ac3a44c803a5f..1f214bc42cbe6 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -28,11 +28,10 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_index::IndexVec; -use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{ - traversal, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, - LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, - SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK, + AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, + MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, + Statement, StatementKind, TerminatorKind, START_BLOCK, }; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_middle::util::Providers; @@ -339,12 +338,15 @@ fn mir_promoted( // Collect `required_consts` *before* promotion, so if there are any consts being promoted // we still add them to the list in the outer MIR body. - let mut required_consts = Vec::new(); - let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); - for (bb, bb_data) in traversal::reverse_postorder(&body) { - required_consts_visitor.visit_basic_block_data(bb, bb_data); + RequiredConstsVisitor::compute_required_consts(&mut body); + // If this has an associated by-move async closure body, that doesn't get run through these + // passes itself, it gets "tagged along" by the pass manager. `RequiredConstsVisitor` is not + // a regular pass so we have to also apply it manually to the other body. + if let Some(coroutine) = body.coroutine.as_mut() { + if let Some(by_move_body) = coroutine.by_move_body.as_mut() { + RequiredConstsVisitor::compute_required_consts(by_move_body); + } } - body.required_consts = required_consts; // What we need to run borrowck etc. let promote_pass = promote_consts::PromoteTemps::default(); @@ -561,9 +563,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tcx, body, &[ - // Before doing anything, remember which items are being mentioned so that the set of items - // visited does not depend on the optimization level. - &mentioned_items::MentionedItems, // Add some UB checks before any UB gets optimized away. &check_alignment::CheckAlignment, // Before inlining: trim down MIR with passes to reduce inlining work. @@ -657,6 +656,19 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { return body; } + // Before doing anything, remember which items are being mentioned so that the set of items + // visited does not depend on the optimization level. + // We do not use `run_passes` for this as that might skip the pass if `injection_phase` is set. + mentioned_items::MentionedItems.run_pass(tcx, &mut body); + // If this has an associated by-move async closure body, that doesn't get run through these + // passes itself, it gets "tagged along" by the pass manager. Since we're not using the pass + // manager we have to do this by hand. + if let Some(coroutine) = body.coroutine.as_mut() { + if let Some(by_move_body) = coroutine.by_move_body.as_mut() { + mentioned_items::MentionedItems.run_pass(tcx, by_move_body); + } + } + // If `mir_drops_elaborated_and_const_checked` found that the current body has unsatisfiable // predicates, it will shrink the MIR to a single `unreachable` terminator. // More generally, if MIR is a lone `unreachable`, there is nothing to optimize. diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index df4f3ccb9b5b8..47758b56f8c90 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -3,8 +3,10 @@ use std::iter; use rustc_index::IndexSlice; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; -use rustc_target::abi::Size; +use rustc_target::abi::Integer; +use rustc_type_ir::TyKind::*; use super::simplify::simplify_cfg; @@ -42,10 +44,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { should_cleanup = true; continue; } - // unsound: https://github.com/rust-lang/rust/issues/124150 - if tcx.sess.opts.unstable_opts.unsound_mir_opts - && SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() - { + if SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() { should_cleanup = true; continue; } @@ -264,33 +263,56 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf { } } +/// Check if the cast constant using `IntToInt` is equal to the target constant. +fn can_cast( + tcx: TyCtxt<'_>, + src_val: impl Into, + src_layout: TyAndLayout<'_>, + cast_ty: Ty<'_>, + target_scalar: ScalarInt, +) -> bool { + let from_scalar = ScalarInt::try_from_uint(src_val.into(), src_layout.size).unwrap(); + let v = match src_layout.ty.kind() { + Uint(_) => from_scalar.to_uint(src_layout.size), + Int(_) => from_scalar.to_int(src_layout.size) as u128, + _ => unreachable!("invalid int"), + }; + let size = match *cast_ty.kind() { + Int(t) => Integer::from_int_ty(&tcx, t).size(), + Uint(t) => Integer::from_uint_ty(&tcx, t).size(), + _ => unreachable!("invalid int"), + }; + let v = size.truncate(v); + let cast_scalar = ScalarInt::try_from_uint(v, size).unwrap(); + cast_scalar == target_scalar +} + #[derive(Default)] struct SimplifyToExp { - transfrom_types: Vec, + transfrom_kinds: Vec, } #[derive(Clone, Copy)] -enum CompareType<'tcx, 'a> { +enum ExpectedTransformKind<'tcx, 'a> { /// Identical statements. Same(&'a StatementKind<'tcx>), /// Assignment statements have the same value. - Eq(&'a Place<'tcx>, Ty<'tcx>, ScalarInt), + SameByEq { place: &'a Place<'tcx>, ty: Ty<'tcx>, scalar: ScalarInt }, /// Enum variant comparison type. - Discr { place: &'a Place<'tcx>, ty: Ty<'tcx>, is_signed: bool }, + Cast { place: &'a Place<'tcx>, ty: Ty<'tcx> }, } -enum TransfromType { +enum TransfromKind { Same, - Eq, - Discr, + Cast, } -impl From> for TransfromType { - fn from(compare_type: CompareType<'_, '_>) -> Self { +impl From> for TransfromKind { + fn from(compare_type: ExpectedTransformKind<'_, '_>) -> Self { match compare_type { - CompareType::Same(_) => TransfromType::Same, - CompareType::Eq(_, _, _) => TransfromType::Eq, - CompareType::Discr { .. } => TransfromType::Discr, + ExpectedTransformKind::Same(_) => TransfromKind::Same, + ExpectedTransformKind::SameByEq { .. } => TransfromKind::Same, + ExpectedTransformKind::Cast { .. } => TransfromKind::Cast, } } } @@ -354,7 +376,7 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { return None; } let mut target_iter = targets.iter(); - let (first_val, first_target) = target_iter.next().unwrap(); + let (first_case_val, first_target) = target_iter.next().unwrap(); let first_terminator_kind = &bbs[first_target].terminator().kind; // Check that destinations are identical, and if not, then don't optimize this block if !targets @@ -364,24 +386,20 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { return None; } - let discr_size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; + let discr_layout = tcx.layout_of(param_env.and(discr_ty)).unwrap(); let first_stmts = &bbs[first_target].statements; - let (second_val, second_target) = target_iter.next().unwrap(); + let (second_case_val, second_target) = target_iter.next().unwrap(); let second_stmts = &bbs[second_target].statements; if first_stmts.len() != second_stmts.len() { return None; } - fn int_equal(l: ScalarInt, r: impl Into, size: Size) -> bool { - l.to_bits_unchecked() == ScalarInt::try_from_uint(r, size).unwrap().to_bits_unchecked() - } - // We first compare the two branches, and then the other branches need to fulfill the same conditions. - let mut compare_types = Vec::new(); + let mut expected_transform_kinds = Vec::new(); for (f, s) in iter::zip(first_stmts, second_stmts) { let compare_type = match (&f.kind, &s.kind) { // If two statements are exactly the same, we can optimize. - (f_s, s_s) if f_s == s_s => CompareType::Same(f_s), + (f_s, s_s) if f_s == s_s => ExpectedTransformKind::Same(f_s), // If two statements are assignments with the match values to the same place, we can optimize. ( @@ -395,22 +413,29 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { f_c.const_.try_eval_scalar_int(tcx, param_env), s_c.const_.try_eval_scalar_int(tcx, param_env), ) { - (Some(f), Some(s)) if f == s => CompareType::Eq(lhs_f, f_c.const_.ty(), f), - // Enum variants can also be simplified to an assignment statement if their values are equal. - // We need to consider both unsigned and signed scenarios here. + (Some(f), Some(s)) if f == s => ExpectedTransformKind::SameByEq { + place: lhs_f, + ty: f_c.const_.ty(), + scalar: f, + }, + // Enum variants can also be simplified to an assignment statement, + // if we can use `IntToInt` cast to get an equal value. (Some(f), Some(s)) - if ((f_c.const_.ty().is_signed() || discr_ty.is_signed()) - && int_equal(f, first_val, discr_size) - && int_equal(s, second_val, discr_size)) - || (Some(f) == ScalarInt::try_from_uint(first_val, f.size()) - && Some(s) - == ScalarInt::try_from_uint(second_val, s.size())) => + if (can_cast( + tcx, + first_case_val, + discr_layout, + f_c.const_.ty(), + f, + ) && can_cast( + tcx, + second_case_val, + discr_layout, + f_c.const_.ty(), + s, + )) => { - CompareType::Discr { - place: lhs_f, - ty: f_c.const_.ty(), - is_signed: f_c.const_.ty().is_signed() || discr_ty.is_signed(), - } + ExpectedTransformKind::Cast { place: lhs_f, ty: f_c.const_.ty() } } _ => { return None; @@ -421,47 +446,36 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { // Otherwise we cannot optimize. Try another block. _ => return None, }; - compare_types.push(compare_type); + expected_transform_kinds.push(compare_type); } // All remaining BBs need to fulfill the same pattern as the two BBs from the previous step. for (other_val, other_target) in target_iter { let other_stmts = &bbs[other_target].statements; - if compare_types.len() != other_stmts.len() { + if expected_transform_kinds.len() != other_stmts.len() { return None; } - for (f, s) in iter::zip(&compare_types, other_stmts) { + for (f, s) in iter::zip(&expected_transform_kinds, other_stmts) { match (*f, &s.kind) { - (CompareType::Same(f_s), s_s) if f_s == s_s => {} + (ExpectedTransformKind::Same(f_s), s_s) if f_s == s_s => {} ( - CompareType::Eq(lhs_f, f_ty, val), + ExpectedTransformKind::SameByEq { place: lhs_f, ty: f_ty, scalar }, StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), ) if lhs_f == lhs_s && s_c.const_.ty() == f_ty - && s_c.const_.try_eval_scalar_int(tcx, param_env) == Some(val) => {} + && s_c.const_.try_eval_scalar_int(tcx, param_env) == Some(scalar) => {} ( - CompareType::Discr { place: lhs_f, ty: f_ty, is_signed }, + ExpectedTransformKind::Cast { place: lhs_f, ty: f_ty }, StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s && s_c.const_.ty() == f_ty => { - let Some(f) = s_c.const_.try_eval_scalar_int(tcx, param_env) else { - return None; - }; - if is_signed - && s_c.const_.ty().is_signed() - && int_equal(f, other_val, discr_size) - { - continue; - } - if Some(f) == ScalarInt::try_from_uint(other_val, f.size()) { - continue; - } - return None; - } + ) if let Some(f) = s_c.const_.try_eval_scalar_int(tcx, param_env) + && lhs_f == lhs_s + && s_c.const_.ty() == f_ty + && can_cast(tcx, other_val, discr_layout, f_ty, f) => {} _ => return None, } } } - self.transfrom_types = compare_types.into_iter().map(|c| c.into()).collect(); + self.transfrom_kinds = expected_transform_kinds.into_iter().map(|c| c.into()).collect(); Some(()) } @@ -479,13 +493,13 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { let (_, first) = targets.iter().next().unwrap(); let first = &bbs[first]; - for (t, s) in iter::zip(&self.transfrom_types, &first.statements) { + for (t, s) in iter::zip(&self.transfrom_kinds, &first.statements) { match (t, &s.kind) { - (TransfromType::Same, _) | (TransfromType::Eq, _) => { + (TransfromKind::Same, _) => { patch.add_statement(parent_end, s.kind.clone()); } ( - TransfromType::Discr, + TransfromKind::Cast, StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), ) => { let operand = Operand::Copy(Place::from(discr_local)); diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index e33bdd9942192..32c8064ebca50 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -23,10 +23,9 @@ impl<'tcx> MirPass<'tcx> for MentionedItems { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { - debug_assert!(body.mentioned_items.is_empty()); let mut mentioned_items = Vec::new(); MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body); - body.mentioned_items = mentioned_items; + body.set_mentioned_items(mentioned_items); } } diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index f8971387ea4d7..dc8e50ac8cd28 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -702,6 +702,9 @@ struct Promoter<'a, 'tcx> { temps: &'a mut IndexVec, extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>, + /// Used to assemble the required_consts list while building the promoted. + required_consts: Vec>, + /// If true, all nested temps are also kept in the /// source MIR, not moved to the promoted MIR. keep_original: bool, @@ -924,11 +927,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let span = self.promoted.span; self.assign(RETURN_PLACE, rvalue, span); - // Now that we did promotion, we know whether we'll want to add this to `required_consts`. + // Now that we did promotion, we know whether we'll want to add this to `required_consts` of + // the surrounding MIR body. if self.add_to_required { - self.source.required_consts.push(promoted_op); + self.source.required_consts.as_mut().unwrap().push(promoted_op); } + self.promoted.set_required_consts(self.required_consts); + self.promoted } } @@ -947,7 +953,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) { if constant.const_.is_required_const() { - self.promoted.required_consts.push(*constant); + self.required_consts.push(*constant); } // Skipping `super_constant` as the visitor is otherwise only looking for locals. @@ -1011,9 +1017,9 @@ fn promote_candidates<'tcx>( extra_statements: &mut extra_statements, keep_original: false, add_to_required: false, + required_consts: Vec::new(), }; - // `required_consts` of the promoted itself gets filled while building the MIR body. let mut promoted = promoter.promote_candidate(candidate, promotions.len()); promoted.source.promoted = Some(promotions.next_index()); promotions.push(promoted); diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index 00bfb5e66008b..50637e2ac03bb 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -1,14 +1,23 @@ use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ConstOperand, Location}; +use rustc_middle::mir::{traversal, Body, ConstOperand, Location}; pub struct RequiredConstsVisitor<'a, 'tcx> { required_consts: &'a mut Vec>, } impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { - pub fn new(required_consts: &'a mut Vec>) -> Self { + fn new(required_consts: &'a mut Vec>) -> Self { RequiredConstsVisitor { required_consts } } + + pub fn compute_required_consts(body: &mut Body<'tcx>) { + let mut required_consts = Vec::new(); + let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); + for (bb, bb_data) in traversal::reverse_postorder(&body) { + required_consts_visitor.visit_basic_block_data(bb, bb_data); + } + body.set_required_consts(required_consts); + } } impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index e2fafa3a1a30b..f41f3ef656c51 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -305,7 +305,7 @@ fn new_body<'tcx>( arg_count: usize, span: Span, ) -> Body<'tcx> { - Body::new( + let mut body = Body::new( source, basic_blocks, IndexVec::from_elem_n( @@ -326,7 +326,10 @@ fn new_body<'tcx>( None, // FIXME(compiler-errors): is this correct? None, - ) + ); + // Shims do not directly mention any consts. + body.set_required_consts(Vec::new()); + body } pub struct DropShimElaborator<'a, 'tcx> { @@ -969,13 +972,16 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { }; let source = MirSource::item(ctor_id); - let body = new_body( + let mut body = new_body( source, IndexVec::from_elem_n(start_block, 1), local_decls, sig.inputs().len(), span, ); + // A constructor doesn't mention any other items (and we don't run the usual optimization passes + // so this would otherwise not get filled). + body.set_mentioned_items(Vec::new()); crate::pass_manager::dump_mir_for_phase_change(tcx, &body); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 99cac67f5b1c1..df2abf6dc9cc4 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1229,7 +1229,7 @@ fn collect_items_of_instance<'tcx>( // Always visit all `required_consts`, so that we evaluate them and abort compilation if any of // them errors. - for const_op in &body.required_consts { + for const_op in body.required_consts() { if let Some(val) = collector.eval_constant(const_op) { collect_const_value(tcx, val, mentioned_items); } @@ -1237,7 +1237,7 @@ fn collect_items_of_instance<'tcx>( // Always gather mentioned items. We try to avoid processing items that we have already added to // `used_items` above. - for item in &body.mentioned_items { + for item in body.mentioned_items() { if !collector.used_mentioned_items.contains(&item.node) { let item_mono = collector.monomorphize(item.node); visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 60beaa0df84ca..e69d8d84d7d89 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -7,7 +7,7 @@ use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::{self as ty, Interner, Upcast as _}; +use rustc_type_ir::{self as ty, elaborate, Interner, Upcast as _}; use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; @@ -671,11 +671,19 @@ where { let cx = ecx.cx(); let mut requirements = vec![]; - requirements.extend( + // Elaborating all supertrait outlives obligations here is not soundness critical, + // since if we just used the unelaborated set, then the transitive supertraits would + // be reachable when proving the former. However, since we elaborate all supertrait + // outlives obligations when confirming impls, we would end up with a different set + // of outlives obligations here if we didn't do the same, leading to ambiguity. + // FIXME(-Znext-solver=coinductive): Adding supertraits here can be removed once we + // make impls coinductive always, since they'll always need to prove their supertraits. + requirements.extend(elaborate::elaborate( + cx, cx.explicit_super_predicates_of(trait_ref.def_id) .iter_instantiated(cx, trait_ref.args) .map(|(pred, _)| pred), - ); + )); // FIXME(associated_const_equality): Also add associated consts to // the requirements here. diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 4474bbc235198..b1dba712f797a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -87,6 +87,19 @@ where .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + // We currently elaborate all supertrait outlives obligations from impls. + // This can be removed when we actually do coinduction correctly, and prove + // all supertrait obligations unconditionally. + let goal_clause: I::Clause = goal.predicate.upcast(cx); + for clause in elaborate::elaborate(cx, [goal_clause]) { + if matches!( + clause.kind().skip_binder(), + ty::ClauseKind::TypeOutlives(..) | ty::ClauseKind::RegionOutlives(..) + ) { + ecx.add_goal(GoalSource::Misc, goal.with(cx, clause)); + } + } + ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) }) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 391a57917768d..8e8d91ce4d038 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -66,7 +66,7 @@ parse_box_not_pat = expected pattern, found {$descr} .suggestion = escape `box` to use it as an identifier parse_box_syntax_removed = `box_syntax` has been removed - .suggestion = use `Box::new()` instead +parse_box_syntax_removed_suggestion = use `Box::new()` instead parse_cannot_be_raw_ident = `{$ident}` cannot be a raw identifier @@ -365,6 +365,7 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment .sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .label = this is not an unsafe attribute .suggestion = remove the `unsafe(...)` .note = extraneous unsafe is not allowed in attributes diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 1076280370815..0d4512be480c3 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -555,12 +555,7 @@ pub(crate) enum MissingInInForLoopSub { code = "in" )] InNotOf(#[primary_span] Span), - #[suggestion( - parse_add_in, - style = "verbose", - applicability = "maybe-incorrect", - code = " in " - )] + #[suggestion(parse_add_in, style = "verbose", applicability = "maybe-incorrect", code = " in ")] AddIn(#[primary_span] Span), } @@ -2730,15 +2725,24 @@ impl HelpUseLatestEdition { #[derive(Diagnostic)] #[diag(parse_box_syntax_removed)] -pub struct BoxSyntaxRemoved<'a> { +pub struct BoxSyntaxRemoved { #[primary_span] - #[suggestion( - code = "Box::new({code})", - applicability = "machine-applicable", - style = "verbose" - )] pub span: Span, - pub code: &'a str, + #[subdiagnostic] + pub sugg: AddBoxNew, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_box_syntax_removed_suggestion, + applicability = "machine-applicable", + style = "verbose" +)] +pub struct AddBoxNew { + #[suggestion_part(code = "Box::new(")] + pub box_kw_and_lo: Span, + #[suggestion_part(code = ")")] + pub hi: Span, } #[derive(Diagnostic)] @@ -3188,6 +3192,7 @@ pub(crate) struct DotDotRangeAttribute { #[note] pub struct InvalidAttrUnsafe { #[primary_span] + #[label] pub span: Span, pub name: Path, } diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 12b9414d1f760..8fdfbcee38546 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -8,7 +8,7 @@ use rustc_span::{sym, BytePos, Span}; use thin_vec::ThinVec; use tracing::debug; -use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle}; +use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle}; use crate::{errors, fluent_generated as fluent, maybe_whole}; // Public for rustfmt usage @@ -31,6 +31,12 @@ enum OuterAttributeType { Attribute, } +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum AllowLeadingUnsafe { + Yes, + No, +} + impl<'a> Parser<'a> { /// Parses attributes that appear before an item. pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> { @@ -307,8 +313,8 @@ impl<'a> Parser<'a> { // inner attribute, for possible later processing in a `LazyAttrTokenStream`. if let Capturing::Yes = self.capture_state.capturing { let end_pos = self.num_bump_calls; - let range = start_pos..end_pos; - self.capture_state.inner_attr_ranges.insert(attr.id, range); + let parser_range = ParserRange(start_pos..end_pos); + self.capture_state.inner_attr_parser_ranges.insert(attr.id, parser_range); } attrs.push(attr); } else { @@ -332,7 +338,7 @@ impl<'a> Parser<'a> { /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited. pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> { - let cfg_predicate = self.parse_meta_item()?; + let cfg_predicate = self.parse_meta_item(AllowLeadingUnsafe::No)?; self.expect(&token::Comma)?; // Presumably, the majority of the time there will only be one attr. @@ -368,7 +374,10 @@ impl<'a> Parser<'a> { /// MetaItem = SimplePath ( '=' UNSUFFIXED_LIT | '(' MetaSeq? ')' )? ; /// MetaSeq = MetaItemInner (',' MetaItemInner)* ','? ; /// ``` - pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { + pub fn parse_meta_item( + &mut self, + unsafe_allowed: AllowLeadingUnsafe, + ) -> PResult<'a, ast::MetaItem> { // We can't use `maybe_whole` here because it would bump in the `None` // case, which we don't want. if let token::Interpolated(nt) = &self.token.kind @@ -384,7 +393,11 @@ impl<'a> Parser<'a> { } let lo = self.token.span; - let is_unsafe = self.eat_keyword(kw::Unsafe); + let is_unsafe = if unsafe_allowed == AllowLeadingUnsafe::Yes { + self.eat_keyword(kw::Unsafe) + } else { + false + }; let unsafety = if is_unsafe { let unsafe_span = self.prev_token.span; self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); @@ -427,7 +440,7 @@ impl<'a> Parser<'a> { Err(err) => err.cancel(), // we provide a better error below } - match self.parse_meta_item() { + match self.parse_meta_item(AllowLeadingUnsafe::No) { Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)), Err(err) => err.cancel(), // we provide a better error below } diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 611dbc0535c61..abf61036c2deb 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -10,7 +10,10 @@ use rustc_errors::PResult; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, DUMMY_SP}; -use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor}; +use super::{ + Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange, + TokenCursor, +}; /// A wrapper type to ensure that the parser handles outer attributes correctly. /// When we parse outer attributes, we need to ensure that we capture tokens @@ -28,8 +31,8 @@ use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCurso #[derive(Debug, Clone)] pub struct AttrWrapper { attrs: AttrVec, - // The start of the outer attributes in the token cursor. - // This allows us to create a `ReplaceRange` for the entire attribute + // The start of the outer attributes in the parser's token stream. + // This lets us create a `NodeReplacement` for the entire attribute // target, including outer attributes. start_pos: u32, } @@ -53,10 +56,9 @@ impl AttrWrapper { /// Prepend `self.attrs` to `attrs`. // FIXME: require passing an NT to prevent misuse of this method - pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) { - let mut self_attrs = self.attrs; - mem::swap(attrs, &mut self_attrs); - attrs.extend(self_attrs); + pub(crate) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) { + mem::swap(attrs, &mut self.attrs); + attrs.extend(self.attrs); } pub fn is_empty(&self) -> bool { @@ -89,7 +91,7 @@ struct LazyAttrTokenStreamImpl { cursor_snapshot: TokenCursor, num_calls: u32, break_last_token: bool, - replace_ranges: Box<[ReplaceRange]>, + node_replacements: Box<[NodeReplacement]>, } impl ToAttrTokenStream for LazyAttrTokenStreamImpl { @@ -104,21 +106,24 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { .chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next()))) .take(self.num_calls as usize); - if self.replace_ranges.is_empty() { + if self.node_replacements.is_empty() { make_attr_token_stream(tokens, self.break_last_token) } else { let mut tokens: Vec<_> = tokens.collect(); - let mut replace_ranges = self.replace_ranges.to_vec(); - replace_ranges.sort_by_key(|(range, _)| range.start); + let mut node_replacements = self.node_replacements.to_vec(); + node_replacements.sort_by_key(|(range, _)| range.0.start); #[cfg(debug_assertions)] - for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { + for [(node_range, tokens), (next_node_range, next_tokens)] in + node_replacements.array_windows() + { assert!( - range.end <= next_range.start || range.end >= next_range.end, - "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", - range, + node_range.0.end <= next_node_range.0.start + || node_range.0.end >= next_node_range.0.end, + "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + node_range, tokens, - next_range, + next_node_range, next_tokens, ); } @@ -136,20 +141,23 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // start position, we ensure that any (outer) replace range which // encloses another (inner) replace range will fully overwrite the // inner range's replacement. - for (range, target) in replace_ranges.into_iter().rev() { - assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); + for (node_range, target) in node_replacements.into_iter().rev() { + assert!( + !node_range.0.is_empty(), + "Cannot replace an empty node range: {:?}", + node_range.0 + ); // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus // enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the // total length of `tokens` constant throughout the replacement process, allowing - // us to use all of the `ReplaceRanges` entries without adjusting indices. + // us to do all replacements without adjusting indices. let target_len = target.is_some() as usize; tokens.splice( - (range.start as usize)..(range.end as usize), - target - .into_iter() - .map(|target| FlatToken::AttrsTarget(target)) - .chain(iter::repeat(FlatToken::Empty).take(range.len() - target_len)), + (node_range.0.start as usize)..(node_range.0.end as usize), + target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain( + iter::repeat(FlatToken::Empty).take(node_range.0.len() - target_len), + ), ); } make_attr_token_stream(tokens.into_iter(), self.break_last_token) @@ -216,7 +224,7 @@ impl<'a> Parser<'a> { let cursor_snapshot = self.token_cursor.clone(); let start_pos = self.num_bump_calls; let has_outer_attrs = !attrs.attrs.is_empty(); - let replace_ranges_start = self.capture_state.replace_ranges.len(); + let parser_replacements_start = self.capture_state.parser_replacements.len(); // We set and restore `Capturing::Yes` on either side of the call to // `f`, so we can distinguish the outermost call to @@ -271,7 +279,7 @@ impl<'a> Parser<'a> { return Ok(ret); } - let replace_ranges_end = self.capture_state.replace_ranges.len(); + let parser_replacements_end = self.capture_state.parser_replacements.len(); assert!( !(self.break_last_token && capture_trailing), @@ -288,15 +296,16 @@ impl<'a> Parser<'a> { let num_calls = end_pos - start_pos; - // Take the captured ranges for any inner attributes that we parsed in - // `Parser::parse_inner_attributes`, and pair them in a `ReplaceRange` - // with `None`, which means the relevant tokens will be removed. (More - // details below.) - let mut inner_attr_replace_ranges = Vec::new(); + // Take the captured `ParserRange`s for any inner attributes that we parsed in + // `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`, + // which means the relevant tokens will be removed. (More details below.) + let mut inner_attr_parser_replacements = Vec::new(); for attr in ret.attrs() { if attr.style == ast::AttrStyle::Inner { - if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) { - inner_attr_replace_ranges.push((attr_range, None)); + if let Some(inner_attr_parser_range) = + self.capture_state.inner_attr_parser_ranges.remove(&attr.id) + { + inner_attr_parser_replacements.push((inner_attr_parser_range, None)); } else { self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute"); } @@ -305,37 +314,41 @@ impl<'a> Parser<'a> { // This is hot enough for `deep-vector` that checking the conditions for an empty iterator // is measurably faster than actually executing the iterator. - let replace_ranges: Box<[ReplaceRange]> = - if replace_ranges_start == replace_ranges_end && inner_attr_replace_ranges.is_empty() { - Box::new([]) - } else { - // Grab any replace ranges that occur *inside* the current AST node. We will - // perform the actual replacement only when we convert the `LazyAttrTokenStream` to - // an `AttrTokenStream`. - self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] - .iter() - .cloned() - .chain(inner_attr_replace_ranges.iter().cloned()) - .map(|(range, data)| ((range.start - start_pos)..(range.end - start_pos), data)) - .collect() - }; + let node_replacements: Box<[_]> = if parser_replacements_start == parser_replacements_end + && inner_attr_parser_replacements.is_empty() + { + Box::new([]) + } else { + // Grab any replace ranges that occur *inside* the current AST node. Convert them + // from `ParserRange` form to `NodeRange` form. We will perform the actual + // replacement only when we convert the `LazyAttrTokenStream` to an + // `AttrTokenStream`. + self.capture_state.parser_replacements + [parser_replacements_start..parser_replacements_end] + .iter() + .cloned() + .chain(inner_attr_parser_replacements.iter().cloned()) + .map(|(parser_range, data)| (NodeRange::new(parser_range, start_pos), data)) + .collect() + }; // What is the status here when parsing the example code at the top of this method? // // When parsing `g`: // - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr). - // - `inner_attr_replace_ranges` has one entry (`5..15`, when counting from `fn`), to + // - `inner_attr_parser_replacements` has one entry (`ParserRange(17..27)`), to // delete the inner attr's tokens. - // - This entry is put into the lazy tokens for `g`, i.e. deleting the inner attr from - // those tokens (if they get evaluated). + // - This entry is converted to `NodeRange(5..15)` (relative to the `fn`) and put into + // the lazy tokens for `g`, i.e. deleting the inner attr from those tokens (if they get + // evaluated). // - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s // replace ranges at the bottom of this function, for processing when parsing `m`. - // - `replace_ranges_start..replace_ranges_end` is empty. + // - `parser_replacements_start..parser_replacements_end` is empty. // // When parsing `m`: // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute). - // - `inner_attr_replace_ranges` is empty. - // - `replace_range_start..replace_ranges_end` has one entry. + // - `inner_attr_parser_replacements` is empty. + // - `parser_replacements_start..parser_replacements_end` has one entry. // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`, // including its outer attribute), with: // - `attrs`: includes the outer and the inner attr. @@ -346,7 +359,7 @@ impl<'a> Parser<'a> { num_calls, cursor_snapshot, break_last_token: self.break_last_token, - replace_ranges, + node_replacements, }); // If we support tokens and don't already have them, store the newly captured tokens. @@ -367,7 +380,7 @@ impl<'a> Parser<'a> { // What is the status here when parsing the example code at the top of this method? // // When parsing `g`, we add one entry: - // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with: + // - The pushed entry (`ParserRange(3..33)`) has a new `AttrsTarget` with: // - `attrs`: includes the outer and the inner attr. // - `tokens`: lazy tokens for `g` (with its inner attr deleted). // @@ -378,12 +391,14 @@ impl<'a> Parser<'a> { // cfg-expand this AST node. let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; - self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); + self.capture_state + .parser_replacements + .push((ParserRange(start_pos..end_pos), Some(target))); } else if matches!(self.capture_state.capturing, Capturing::No) { // Only clear the ranges once we've finished capturing entirely, i.e. we've finished // the outermost call to this method. - self.capture_state.replace_ranges.clear(); - self.capture_state.inner_attr_ranges.clear(); + self.capture_state.parser_replacements.clear(); + self.capture_state.inner_attr_parser_ranges.clear(); } Ok(ret) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a242dc5cd582d..ccf8dcdf0b6f2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -40,14 +40,6 @@ use super::{ }; use crate::{errors, maybe_recover_from_interpolated_ty_qpath}; -#[derive(Debug)] -pub(super) enum LhsExpr { - // Already parsed just the outer attributes. - Unparsed { attrs: AttrWrapper }, - // Already parsed the expression. - Parsed { expr: P, starts_statement: bool }, -} - #[derive(Debug)] enum DestructuredFloat { /// 1e2 @@ -113,30 +105,31 @@ impl<'a> Parser<'a> { r: Restrictions, attrs: AttrWrapper, ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs })) + self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs)) } /// Parses an associative expression with operators of at least `min_prec` precedence. pub(super) fn parse_expr_assoc_with( &mut self, min_prec: usize, - lhs: LhsExpr, + attrs: AttrWrapper, ) -> PResult<'a, P> { - let mut starts_stmt = false; - let mut lhs = match lhs { - LhsExpr::Parsed { expr, starts_statement } => { - starts_stmt = starts_statement; - expr - } - LhsExpr::Unparsed { attrs } => { - if self.token.is_range_separator() { - return self.parse_expr_prefix_range(attrs); - } else { - self.parse_expr_prefix(attrs)? - } - } + let lhs = if self.token.is_range_separator() { + return self.parse_expr_prefix_range(attrs); + } else { + self.parse_expr_prefix(attrs)? }; + self.parse_expr_assoc_rest_with(min_prec, false, lhs) + } + /// Parses the rest of an associative expression (i.e. the part after the lhs) with operators + /// of at least `min_prec` precedence. + pub(super) fn parse_expr_assoc_rest_with( + &mut self, + min_prec: usize, + starts_stmt: bool, + mut lhs: P, + ) -> PResult<'a, P> { if !self.should_continue_as_assoc_expr(&lhs) { return Ok(lhs); } @@ -272,7 +265,7 @@ impl<'a> Parser<'a> { }; let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { let attrs = this.parse_outer_attributes()?; - this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::Unparsed { attrs }) + this.parse_expr_assoc_with(prec + prec_adjustment, attrs) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -447,7 +440,7 @@ impl<'a> Parser<'a> { let maybe_lt = self.token.clone(); let attrs = self.parse_outer_attributes()?; Some( - self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs }) + self.parse_expr_assoc_with(prec + 1, attrs) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?, ) } else { @@ -504,12 +497,9 @@ impl<'a> Parser<'a> { let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. let attrs = this.parse_outer_attributes()?; - this.parse_expr_assoc_with( - op.unwrap().precedence() + 1, - LhsExpr::Unparsed { attrs }, - ) - .map(|x| (lo.to(x.span), Some(x))) - .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? + this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs) + .map(|x| (lo.to(x.span), Some(x))) + .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? } else { (lo, None) }; @@ -618,10 +608,12 @@ impl<'a> Parser<'a> { /// Parse `box expr` - this syntax has been removed, but we still parse this /// for now to provide a more useful error fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> { - let (span, _) = self.parse_expr_prefix_common(box_kw)?; - let inner_span = span.with_lo(box_kw.hi()); - let code = self.psess.source_map().span_to_snippet(inner_span).unwrap(); - let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() }); + let (span, expr) = self.parse_expr_prefix_common(box_kw)?; + // Make a multipart suggestion instead of `span_to_snippet` in case source isn't available + let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr)); + let hi = span.shrink_to_hi(); + let sugg = errors::AddBoxNew { box_kw_and_lo, hi }; + let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span, sugg }); Ok((span, ExprKind::Err(guar))) } @@ -887,7 +879,7 @@ impl<'a> Parser<'a> { mut e: P, lo: Span, ) -> PResult<'a, P> { - let res = ensure_sufficient_stack(|| { + let mut res = ensure_sufficient_stack(|| { loop { let has_question = if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) { @@ -934,17 +926,13 @@ impl<'a> Parser<'a> { // Stitch the list of outer attributes onto the return value. A little // bit ugly, but the best way given the current code structure. - if attrs.is_empty() { - res - } else { - res.map(|expr| { - expr.map(|mut expr| { - attrs.extend(expr.attrs); - expr.attrs = attrs; - expr - }) - }) + if !attrs.is_empty() + && let Ok(expr) = &mut res + { + mem::swap(&mut expr.attrs, &mut attrs); + expr.attrs.extend(attrs) } + res } pub(super) fn parse_dot_suffix_expr( @@ -2645,10 +2633,7 @@ impl<'a> Parser<'a> { self.expect(&token::Eq)?; } let attrs = self.parse_outer_attributes()?; - let expr = self.parse_expr_assoc_with( - 1 + prec_let_scrutinee_needs_par(), - LhsExpr::Unparsed { attrs }, - )?; + let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } @@ -3153,7 +3138,8 @@ impl<'a> Parser<'a> { if !require_comma { arm_body = Some(expr); - this.eat(&token::Comma); + // Eat a comma if it exists, though. + let _ = this.eat(&token::Comma); Ok(Recovered::No) } else if let Some((span, guar)) = this.parse_arm_body_missing_braces(&expr, arrow_span) @@ -3654,7 +3640,7 @@ impl<'a> Parser<'a> { fields.push(f); } self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); - self.eat(&token::Comma); + let _ = self.eat(&token::Comma); } } } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index a67e3d05551db..9124c15707de5 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -178,7 +178,8 @@ impl<'a> Parser<'a> { span: this.prev_token.span, }); - this.eat(&token::Comma); + // Eat a trailing comma, if it exists. + let _ = this.eat(&token::Comma); } let param = if this.check_lifetime() { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 68da055494528..baa5eb2df635d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1192,13 +1192,14 @@ impl<'a> Parser<'a> { mut safety: Safety, ) -> PResult<'a, ItemInfo> { let abi = self.parse_abi(); // ABI? + // FIXME: This recovery should be tested better. if safety == Safety::Default && self.token.is_keyword(kw::Unsafe) && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace)) { self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err().emit(); safety = Safety::Unsafe(self.token.span); - self.eat_keyword(kw::Unsafe); + let _ = self.eat_keyword(kw::Unsafe); } let module = ast::ForeignMod { safety, @@ -1759,7 +1760,7 @@ impl<'a> Parser<'a> { } } } - self.eat(&token::CloseDelim(Delimiter::Brace)); + self.expect(&token::CloseDelim(Delimiter::Brace))?; } else { let token_str = super::token_descr(&self.token); let where_str = if parsed_where { "" } else { "`where`, or " }; @@ -1902,7 +1903,7 @@ impl<'a> Parser<'a> { if let Some(_guar) = guar { // Handle a case like `Vec>,` where we can continue parsing fields // after the comma - self.eat(&token::Comma); + let _ = self.eat(&token::Comma); // `check_trailing_angle_brackets` already emitted a nicer error, as // proven by the presence of `_guar`. We can continue parsing. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 26ee5bfdee42c..4b8e4c25e16c2 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -192,24 +192,54 @@ struct ClosureSpans { body: Span, } -/// Indicates a range of tokens that should be replaced by -/// the tokens in the provided `AttrsTarget`. This is used in two -/// places during token collection: +/// A token range within a `Parser`'s full token stream. +#[derive(Clone, Debug)] +struct ParserRange(Range); + +/// A token range within an individual AST node's (lazy) token stream, i.e. +/// relative to that node's first token. Distinct from `ParserRange` so the two +/// kinds of range can't be mixed up. +#[derive(Clone, Debug)] +struct NodeRange(Range); + +/// Indicates a range of tokens that should be replaced by an `AttrsTarget` +/// (replacement) or be replaced by nothing (deletion). This is used in two +/// places during token collection. +/// +/// 1. Replacement. During the parsing of an AST node that may have a +/// `#[derive]` attribute, when we parse a nested AST node that has `#[cfg]` +/// or `#[cfg_attr]`, we replace the entire inner AST node with +/// `FlatToken::AttrsTarget`. This lets us perform eager cfg-expansion on an +/// `AttrTokenStream`. /// -/// 1. During the parsing of an AST node that may have a `#[derive]` -/// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]` -/// In this case, we use a `ReplaceRange` to replace the entire inner AST node -/// with `FlatToken::AttrsTarget`, allowing us to perform eager cfg-expansion -/// on an `AttrTokenStream`. +/// 2. Deletion. We delete inner attributes from all collected token streams, +/// and instead track them through the `attrs` field on the AST node. This +/// lets us manipulate them similarly to outer attributes. When we create a +/// `TokenStream`, the inner attributes are inserted into the proper place +/// in the token stream. /// -/// 2. When we parse an inner attribute while collecting tokens. We -/// remove inner attributes from the token stream entirely, and -/// instead track them through the `attrs` field on the AST node. -/// This allows us to easily manipulate them (for example, removing -/// the first macro inner attribute to invoke a proc-macro). -/// When create a `TokenStream`, the inner attributes get inserted -/// into the proper place in the token stream. -type ReplaceRange = (Range, Option); +/// Each replacement starts off in `ParserReplacement` form but is converted to +/// `NodeReplacement` form when it is attached to a single AST node, via +/// `LazyAttrTokenStreamImpl`. +type ParserReplacement = (ParserRange, Option); + +/// See the comment on `ParserReplacement`. +type NodeReplacement = (NodeRange, Option); + +impl NodeRange { + // Converts a range within a parser's tokens to a range within a + // node's tokens beginning at `start_pos`. + // + // For example, imagine a parser with 50 tokens in its token stream, a + // function that spans `ParserRange(20..40)` and an inner attribute within + // that function that spans `ParserRange(30..35)`. We would find the inner + // attribute's range within the function's tokens by subtracting 20, which + // is the position of the function's start token. This gives + // `NodeRange(10..15)`. + fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange { + NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos)) + } +} /// Controls how we capture tokens. Capturing can be expensive, /// so we try to avoid performing capturing in cases where @@ -226,8 +256,8 @@ enum Capturing { #[derive(Clone, Debug)] struct CaptureState { capturing: Capturing, - replace_ranges: Vec, - inner_attr_ranges: FxHashMap>, + parser_replacements: Vec, + inner_attr_parser_ranges: FxHashMap, } /// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that @@ -417,8 +447,8 @@ impl<'a> Parser<'a> { subparser_name, capture_state: CaptureState { capturing: Capturing::No, - replace_ranges: Vec::new(), - inner_attr_ranges: Default::default(), + parser_replacements: Vec::new(), + inner_attr_parser_ranges: Default::default(), }, current_closure: None, recovery: Recovery::Allowed, @@ -547,6 +577,7 @@ impl<'a> Parser<'a> { } #[inline] + #[must_use] fn check_noexpect(&self, tok: &TokenKind) -> bool { self.token == *tok } @@ -556,6 +587,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] + #[must_use] fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { @@ -566,6 +598,7 @@ impl<'a> Parser<'a> { /// Consumes a token 'tok' if it exists. Returns whether the given token was present. #[inline] + #[must_use] pub fn eat(&mut self, tok: &TokenKind) -> bool { let is_present = self.check(tok); if is_present { @@ -577,12 +610,14 @@ 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] + #[must_use] fn check_keyword(&mut self, kw: Symbol) -> bool { self.expected_tokens.push(TokenType::Keyword(kw)); self.token.is_keyword(kw) } #[inline] + #[must_use] fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; @@ -602,6 +637,7 @@ impl<'a> Parser<'a> { /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustc_builtin_macros and rustfmt usage. #[inline] + #[must_use] pub fn eat_keyword(&mut self, kw: Symbol) -> bool { if self.check_keyword(kw) { self.bump(); @@ -615,6 +651,7 @@ impl<'a> Parser<'a> { /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. #[inline] + #[must_use] fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; @@ -636,6 +673,7 @@ impl<'a> Parser<'a> { /// Otherwise, returns `false`. No expectation is added. // Public for rustc_builtin_macros usage. #[inline] + #[must_use] pub fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -648,7 +686,7 @@ impl<'a> Parser<'a> { /// If the given word is not a keyword, signals an error. /// If the next token is not the given word, signals an error. /// Otherwise, eats it. - fn expect_keyword(&mut self, kw: Symbol) -> PResult<'a, ()> { + pub fn expect_keyword(&mut self, kw: Symbol) -> PResult<'a, ()> { if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) } } @@ -1025,8 +1063,11 @@ impl<'a> Parser<'a> { f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing)> { let (val, trailing, recovered) = self.parse_seq_to_before_end(ket, sep, f)?; - if matches!(recovered, Recovered::No) { - self.eat(ket); + if matches!(recovered, Recovered::No) && !self.eat(ket) { + self.dcx().span_delayed_bug( + self.token.span, + "recovered but `parse_seq_to_before_end` did not give us the ket token", + ); } Ok((val, trailing)) } @@ -1209,9 +1250,6 @@ impl<'a> Parser<'a> { if self.eat_keyword_case(kw::Unsafe, case) { Safety::Unsafe(self.prev_token.uninterpolated_span()) } else if self.eat_keyword_case(kw::Safe, case) { - self.psess - .gated_spans - .gate(sym::unsafe_extern_blocks, self.prev_token.uninterpolated_span()); Safety::Safe(self.prev_token.uninterpolated_span()) } else { Safety::Default @@ -1250,7 +1288,7 @@ impl<'a> Parser<'a> { if pat { self.psess.gated_spans.gate(sym::inline_const_pat, span); } - self.eat_keyword(kw::Const); + self.expect_keyword(kw::Const)?; let (attrs, blk) = self.parse_inner_attrs_and_block()?; let anon_const = AnonConst { id: DUMMY_NODE_ID, diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index bd9d96ba573f7..999f6f0eeb0ce 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -39,6 +39,7 @@ impl<'a> Parser<'a> { } match kind { + // `expr_2021` and earlier NonterminalKind::Expr(Expr2021 { .. }) => { token.can_begin_expr() // This exception is here for backwards compatibility. @@ -46,8 +47,16 @@ impl<'a> Parser<'a> { // This exception is here for backwards compatibility. && !token.is_keyword(kw::Const) } + // Current edition expressions NonterminalKind::Expr(Expr) => { - token.can_begin_expr() + // In Edition 2024, `_` is considered an expression, so we + // need to allow it here because `token.can_begin_expr()` does + // not consider `_` to be an expression. + // + // Because `can_begin_expr` is used elsewhere, we need to reduce + // the scope of where the `_` is considered an expression to + // just macro parsing code. + (token.can_begin_expr() || token.is_keyword(kw::Underscore)) // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b6f85cc90324c..5bfb8bdf776a0 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -25,7 +25,7 @@ use crate::errors::{ UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; -use crate::parser::expr::{could_be_unclosed_char_literal, LhsExpr}; +use crate::parser::expr::could_be_unclosed_char_literal; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; #[derive(PartialEq, Copy, Clone)] @@ -403,8 +403,9 @@ impl<'a> Parser<'a> { // Parse an associative expression such as `+ expr`, `% expr`, ... // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - let lhs = LhsExpr::Parsed { expr, starts_statement: false }; - if let Ok(expr) = snapshot.parse_expr_assoc_with(0, lhs).map_err(|err| err.cancel()) { + if let Ok(expr) = + snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) + { // We got a valid expression. self.restore_snapshot(snapshot); self.restrictions.remove(Restrictions::IS_PAT); diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index c5111226d3783..70d2c98d4f1bd 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -313,7 +313,8 @@ impl<'a> Parser<'a> { } // Generic arguments are found - `<`, `(`, `::<` or `::(`. - self.eat(&token::PathSep); + // First, eat `::` if it exists. + let _ = self.eat(&token::PathSep); let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 7b0daaa14335f..b3efb87a4a26a 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -17,7 +17,6 @@ use thin_vec::{thin_vec, ThinVec}; use super::attr::InnerAttrForbiddenReason; use super::diagnostics::AttemptLocalParseRecovery; -use super::expr::LhsExpr; use super::pat::{PatternLocation, RecoverComma}; use super::path::PathStyle; use super::{ @@ -66,7 +65,12 @@ impl<'a> Parser<'a> { } Ok(Some(if self.token.is_keyword(kw::Let) { - self.parse_local_mk(lo, attrs, capture_semi, force_collect)? + self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { + this.expect_keyword(kw::Let)?; + let local = this.parse_local(attrs)?; + let trailing = capture_semi && this.token.kind == token::Semi; + Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing)) + })? } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() { self.recover_stmt_local_after_let( lo, @@ -112,13 +116,12 @@ impl<'a> Parser<'a> { } } } else if let Some(item) = self.parse_item_common( - attrs.clone(), + attrs.clone(), // FIXME: unwanted clone of attrs false, true, FnParseMode { req_name: |_| true, req_body: true }, force_collect, )? { - // FIXME: Bad copy of attrs self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) } else if self.eat(&token::Semi) { // Do not attempt to parse an expression if we're done here. @@ -173,7 +176,7 @@ impl<'a> Parser<'a> { // Perform this outside of the `collect_tokens_trailing_token` closure, // since our outer attributes do not apply to this part of the expression let expr = self.with_res(Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with(0, LhsExpr::Parsed { expr, starts_statement: true }) + this.parse_expr_assoc_rest_with(0, true, expr) })?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) } else { @@ -206,8 +209,7 @@ impl<'a> Parser<'a> { let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?; - let e = self - .parse_expr_assoc_with(0, LhsExpr::Parsed { expr: e, starts_statement: false })?; + let e = self.parse_expr_assoc_rest_with(0, false, e)?; StmtKind::Expr(e) }; Ok(self.mk_stmt(lo.to(hi), kind)) @@ -247,21 +249,6 @@ impl<'a> Parser<'a> { Ok(stmt) } - fn parse_local_mk( - &mut self, - lo: Span, - attrs: AttrWrapper, - capture_semi: bool, - force_collect: ForceCollect, - ) -> PResult<'a, Stmt> { - self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { - this.expect_keyword(kw::Let)?; - let local = this.parse_local(attrs)?; - let trailing = capture_semi && this.token.kind == token::Semi; - Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing)) - }) - } - /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 356bc9a410d6b..a64c00f3b6cbc 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -26,76 +26,35 @@ pub fn check_attr(features: &Features, psess: &ParseSess, attr: &Attribute) { let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); let attr_item = attr.get_normal_item(); - let is_unsafe_attr = attr_info.is_some_and(|attr| attr.safety == AttributeSafety::Unsafe); - - if features.unsafe_attributes { - if is_unsafe_attr { - if let ast::Safety::Default = attr_item.unsafety { - let path_span = attr_item.path.span; - - // If the `attr_item`'s span is not from a macro, then just suggest - // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the - // `unsafe(`, `)` right after and right before the opening and closing - // square bracket respectively. - let diag_span = if attr_item.span().can_be_used_for_suggestions() { - attr_item.span() - } else { - attr.span - .with_lo(attr.span.lo() + BytePos(2)) - .with_hi(attr.span.hi() - BytePos(1)) - }; - - if attr.span.at_least_rust_2024() { - psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { - span: path_span, - suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion { - left: diag_span.shrink_to_lo(), - right: diag_span.shrink_to_hi(), - }, - }); - } else { - psess.buffer_lint( - UNSAFE_ATTR_OUTSIDE_UNSAFE, - path_span, - ast::CRATE_NODE_ID, - BuiltinLintDiag::UnsafeAttrOutsideUnsafe { - attribute_name_span: path_span, - sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), - }, - ); - } - } - } else { - if let Safety::Unsafe(unsafe_span) = attr_item.unsafety { - psess.dcx().emit_err(errors::InvalidAttrUnsafe { - span: unsafe_span, - name: attr_item.path.clone(), - }); - } - } - } + // All non-builtin attributes are considered safe + let safety = attr_info.map(|x| x.safety).unwrap_or(AttributeSafety::Normal); + check_attribute_safety(features, psess, safety, attr); // Check input tokens for built-in and key-value attributes. match attr_info { // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { match parse_meta(psess, attr) { - Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, *name, *template), + // Don't check safety again, we just did that + Ok(meta) => check_builtin_meta_item( + features, psess, &meta, attr.style, *name, *template, false, + ), Err(err) => { err.emit(); } } } - _ if let AttrArgs::Eq(..) = attr_item.args => { - // All key-value attributes are restricted to meta-item syntax. - match parse_meta(psess, attr) { - Ok(_) => {} - Err(err) => { - err.emit(); + _ => { + if let AttrArgs::Eq(..) = attr_item.args { + // All key-value attributes are restricted to meta-item syntax. + match parse_meta(psess, attr) { + Ok(_) => {} + Err(err) => { + err.emit(); + } } } } - _ => {} } } @@ -198,12 +157,85 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte } } +pub fn check_attribute_safety( + features: &Features, + psess: &ParseSess, + safety: AttributeSafety, + attr: &Attribute, +) { + if !features.unsafe_attributes { + return; + } + + let attr_item = attr.get_normal_item(); + + if safety == AttributeSafety::Unsafe { + if let ast::Safety::Default = attr_item.unsafety { + let path_span = attr_item.path.span; + + // If the `attr_item`'s span is not from a macro, then just suggest + // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the + // `unsafe(`, `)` right after and right before the opening and closing + // square bracket respectively. + let diag_span = if attr_item.span().can_be_used_for_suggestions() { + attr_item.span() + } else { + attr.span.with_lo(attr.span.lo() + BytePos(2)).with_hi(attr.span.hi() - BytePos(1)) + }; + + if attr.span.at_least_rust_2024() { + psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { + span: path_span, + suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion { + left: diag_span.shrink_to_lo(), + right: diag_span.shrink_to_hi(), + }, + }); + } else { + psess.buffer_lint( + UNSAFE_ATTR_OUTSIDE_UNSAFE, + path_span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::UnsafeAttrOutsideUnsafe { + attribute_name_span: path_span, + sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), + }, + ); + } + } + } else { + if let Safety::Unsafe(unsafe_span) = attr_item.unsafety { + psess.dcx().emit_err(errors::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_item.path.clone(), + }); + } + } +} + +// Called by `check_builtin_meta_item` and code that manually denies +// `unsafe(...)` in `cfg` +pub fn deny_builtin_meta_unsafety(features: &Features, psess: &ParseSess, meta: &MetaItem) { + // This only supports denying unsafety right now - making builtin attributes + // support unsafety will requite us to thread the actual `Attribute` through + // for the nice diagnostics. + if features.unsafe_attributes { + if let Safety::Unsafe(unsafe_span) = meta.unsafety { + psess + .dcx() + .emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() }); + } + } +} + pub fn check_builtin_meta_item( + features: &Features, psess: &ParseSess, meta: &MetaItem, style: ast::AttrStyle, name: Symbol, template: AttributeTemplate, + deny_unsafety: bool, ) { // Some special attributes like `cfg` must be checked // before the generic check, so we skip them here. @@ -212,6 +244,10 @@ pub fn check_builtin_meta_item( if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) { emit_malformed_attribute(psess, style, meta.span, name, template); } + + if deny_unsafety { + deny_builtin_meta_unsafety(features, psess, meta); + } } fn emit_malformed_attribute( diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index bfe0d54e64521..59c9d1e49f5b3 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -100,6 +100,10 @@ passes_continue_labeled_block = .label = labeled blocks cannot be `continue`'d .block_label = labeled block the `continue` points to +passes_coroutine_on_non_closure = + attribute should be applied to closures + .label = not a closure + passes_coverage_not_fn_or_closure = attribute should be applied to a function definition or closure .label = not a function or closure @@ -542,6 +546,10 @@ passes_only_has_effect_on = *[unspecified] (unspecified--this is a compiler bug) } +passes_optimize_not_fn_or_closure = + attribute should be applied to function or closure + .label = not a function or closure + passes_outer_crate_level_attr = crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a58c57041f830..cf7d724ab63ae 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -20,13 +20,13 @@ use rustc_hir::{ TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, }; use rustc_macros::LintDiagnostic; -use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, @@ -116,132 +116,185 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { match attr.path().as_slice() { - [sym::diagnostic, sym::do_not_recommend] => { + [sym::diagnostic, sym::do_not_recommend, ..] => { self.check_do_not_recommend(attr.span, hir_id, target) } - [sym::diagnostic, sym::on_unimplemented] => { + [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span, hir_id, target) } - [sym::inline] => self.check_inline(hir_id, attr, span, target), - [sym::coverage] => self.check_coverage(attr, span, target), - [sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target), - [sym::marker] => self.check_marker(hir_id, attr, span, target), - [sym::target_feature] => { + [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), + [sym::coverage, ..] => self.check_coverage(attr, span, target), + [sym::optimize, ..] => self.check_optimize(hir_id, attr, target), + [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), + [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), + [sym::target_feature, ..] => { self.check_target_feature(hir_id, attr, span, target, attrs) } - [sym::thread_local] => self.check_thread_local(attr, span, target), - [sym::track_caller] => { + [sym::thread_local, ..] => self.check_thread_local(attr, span, target), + [sym::track_caller, ..] => { self.check_track_caller(hir_id, attr.span, attrs, span, target) } - [sym::doc] => self.check_doc_attrs( + [sym::doc, ..] => self.check_doc_attrs( attr, hir_id, target, &mut specified_inline, &mut doc_aliases, ), - [sym::no_link] => self.check_no_link(hir_id, attr, span, target), - [sym::export_name] => self.check_export_name(hir_id, attr, span, target), - [sym::rustc_layout_scalar_valid_range_start] - | [sym::rustc_layout_scalar_valid_range_end] => { + [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), + [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target), + [sym::rustc_layout_scalar_valid_range_start, ..] + | [sym::rustc_layout_scalar_valid_range_end, ..] => { self.check_rustc_layout_scalar_valid_range(attr, span, target) } - [sym::allow_internal_unstable] => { + [sym::allow_internal_unstable, ..] => { self.check_allow_internal_unstable(hir_id, attr, span, target, attrs) } - [sym::debugger_visualizer] => self.check_debugger_visualizer(attr, target), - [sym::rustc_allow_const_fn_unstable] => { + [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), + [sym::rustc_allow_const_fn_unstable, ..] => { self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) } - [sym::rustc_std_internal_symbol] => { + [sym::rustc_std_internal_symbol, ..] => { self.check_rustc_std_internal_symbol(attr, span, target) } - [sym::naked] => self.check_naked(hir_id, attr, span, target, attrs), - [sym::rustc_never_returns_null_ptr] => { + [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), + [sym::rustc_never_returns_null_ptr, ..] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } - [sym::rustc_legacy_const_generics] => { + [sym::rustc_legacy_const_generics, ..] => { self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) } - [sym::rustc_lint_query_instability] => { + [sym::rustc_lint_query_instability, ..] => { self.check_rustc_lint_query_instability(hir_id, attr, span, target) } - [sym::rustc_lint_diagnostics] => { + [sym::rustc_lint_diagnostics, ..] => { self.check_rustc_lint_diagnostics(hir_id, attr, span, target) } - [sym::rustc_lint_opt_ty] => self.check_rustc_lint_opt_ty(attr, span, target), - [sym::rustc_lint_opt_deny_field_access] => { + [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), + [sym::rustc_lint_opt_deny_field_access, ..] => { self.check_rustc_lint_opt_deny_field_access(attr, span, target) } - [sym::rustc_clean] - | [sym::rustc_dirty] - | [sym::rustc_if_this_changed] - | [sym::rustc_then_this_would_need] => self.check_rustc_dirty_clean(attr), - [sym::rustc_coinductive] - | [sym::rustc_must_implement_one_of] - | [sym::rustc_deny_explicit_impl] - | [sym::const_trait] => self.check_must_be_applied_to_trait(attr, span, target), - [sym::cmse_nonsecure_entry] => { + [sym::rustc_clean, ..] + | [sym::rustc_dirty, ..] + | [sym::rustc_if_this_changed, ..] + | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), + [sym::rustc_coinductive, ..] + | [sym::rustc_must_implement_one_of, ..] + | [sym::rustc_deny_explicit_impl, ..] + | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), + [sym::cmse_nonsecure_entry, ..] => { self.check_cmse_nonsecure_entry(hir_id, attr, span, target) } - [sym::collapse_debuginfo] => self.check_collapse_debuginfo(attr, span, target), - [sym::must_not_suspend] => self.check_must_not_suspend(attr, span, target), - [sym::must_use] => self.check_must_use(hir_id, attr, target), - [sym::rustc_pass_by_value] => self.check_pass_by_value(attr, span, target), - [sym::rustc_allow_incoherent_impl] => { + [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), + [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), + [sym::must_use, ..] => self.check_must_use(hir_id, attr, target), + [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), + [sym::rustc_allow_incoherent_impl, ..] => { self.check_allow_incoherent_impl(attr, span, target) } - [sym::rustc_has_incoherent_inherent_impls] => { + [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - [sym::ffi_pure] => self.check_ffi_pure(attr.span, attrs, target), - [sym::ffi_const] => self.check_ffi_const(attr.span, target), - [sym::rustc_const_unstable] - | [sym::rustc_const_stable] - | [sym::unstable] - | [sym::stable] - | [sym::rustc_allowed_through_unstable_modules] - | [sym::rustc_promotable] => self.check_stability_promotable(attr, target), - [sym::link_ordinal] => self.check_link_ordinal(attr, span, target), - [sym::rustc_confusables] => self.check_confusables(attr, target), - [sym::rustc_safe_intrinsic] => { + [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span, attrs, target), + [sym::ffi_const, ..] => self.check_ffi_const(attr.span, target), + [sym::rustc_const_unstable, ..] + | [sym::rustc_const_stable, ..] + | [sym::unstable, ..] + | [sym::stable, ..] + | [sym::rustc_allowed_through_unstable_modules, ..] + | [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target), + [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), + [sym::rustc_confusables, ..] => self.check_confusables(attr, target), + [sym::rustc_safe_intrinsic, ..] => { self.check_rustc_safe_intrinsic(hir_id, attr, span, target) } - _ => true, - }; - - // lint-only checks - match attr.name_or_empty() { - sym::cold => self.check_cold(hir_id, attr, span, target), - sym::link => self.check_link(hir_id, attr, span, target), - sym::link_name => self.check_link_name(hir_id, attr, span, target), - sym::link_section => self.check_link_section(hir_id, attr, span, target), - sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), - sym::deprecated => self.check_deprecated(hir_id, attr, span, target), - sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), - sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), - sym::macro_export => self.check_macro_export(hir_id, attr, target), - sym::ignore | sym::should_panic => { + [sym::cold, ..] => self.check_cold(hir_id, attr, span, target), + [sym::link, ..] => self.check_link(hir_id, attr, span, target), + [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target), + [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), + [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target), + [sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target), + [sym::macro_use, ..] | [sym::macro_escape, ..] => { + self.check_macro_use(hir_id, attr, target) + } + [sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod), + [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), + [sym::ignore, ..] | [sym::should_panic, ..] => { self.check_generic_attr(hir_id, attr, target, Target::Fn) } - sym::automatically_derived => { + [sym::automatically_derived, ..] => { self.check_generic_attr(hir_id, attr, target, Target::Impl) } - sym::no_implicit_prelude => { + [sym::no_implicit_prelude, ..] => { self.check_generic_attr(hir_id, attr, target, Target::Mod) } - sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id), - sym::proc_macro => { + [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id), + [sym::proc_macro, ..] => { self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) } - sym::proc_macro_attribute => { + [sym::proc_macro_attribute, ..] => { self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); } - sym::proc_macro_derive => { + [sym::proc_macro_derive, ..] => { self.check_generic_attr(hir_id, attr, target, Target::Fn); self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) } - _ => {} + [sym::coroutine, ..] => { + self.check_coroutine(attr, target); + } + [ + // ok + sym::allow + | sym::expect + | sym::warn + | sym::deny + | sym::forbid + | sym::cfg + | sym::cfg_attr + // need to be fixed + | sym::cfi_encoding // FIXME(cfi_encoding) + | sym::may_dangle // FIXME(dropck_eyepatch) + | sym::pointee // FIXME(derive_smart_pointer) + | sym::linkage // FIXME(linkage) + | sym::no_sanitize // FIXME(no_sanitize) + | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) + | sym::used // handled elsewhere to restrict to static items + | sym::repr // handled elsewhere to restrict to type decls items + | sym::instruction_set // broken on stable!!! + | sym::windows_subsystem // broken on stable!!! + | sym::patchable_function_entry // FIXME(patchable_function_entry) + | sym::deprecated_safe // FIXME(deprecated_safe) + // internal + | sym::prelude_import + | sym::panic_handler + | sym::allow_internal_unsafe + | sym::fundamental + | sym::lang + | sym::needs_allocator + | sym::default_lib_allocator + | sym::start + | sym::custom_mir, + .. + ] => {} + [name, ..] => { + match BUILTIN_ATTRIBUTE_MAP.get(name) { + // checked below + Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {} + Some(_) => { + // FIXME: differentiate between unstable and internal attributes just + // like we do with features instead of just accepting `rustc_` + // attributes by name. That should allow trimming the above list, too. + if !name.as_str().starts_with("rustc_") { + span_bug!( + attr.span, + "builtin attribute {name:?} not handled by `CheckAttrVisitor`" + ) + } + } + None => (), + } + } + [] => unreachable!(), } let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); @@ -297,7 +350,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. - fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) -> bool { + fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) { if !matches!(target, Target::Impl) { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -306,16 +359,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::IncorrectDoNotRecommendLocation, ); } - true } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented( - &self, - attr_span: Span, - hir_id: HirId, - target: Target, - ) -> bool { + fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -324,68 +371,82 @@ impl<'tcx> CheckAttrVisitor<'tcx> { DiagnosticOnUnimplementedOnlyForTraits, ); } - true } - /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. - fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if an `#[inline]` is applied to a function or a closure. + fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { Target::Fn | Target::Closure - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span, errors::IgnoredInlineAttrFnProto, - ); - true + ) } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with // just a lint, because we previously erroneously allowed it and some crates used it // accidentally, to be compatible with crates depending on them, we can't throw an // error here. - Target::AssocConst => { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span, - errors::IgnoredInlineAttrConstants, - ); - true - } + Target::AssocConst => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrConstants, + ), // FIXME(#80564): Same for fields, arms, and macro defs Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline"); - true + self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline") } _ => { self.dcx().emit_err(errors::InlineNotFnOrClosure { attr_span: attr.span, defn_span: span, }); - false } } } /// Checks that `#[coverage(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_coverage(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_coverage(&self, attr: &Attribute, span: Span, target: Target) { match target { Target::Fn | Target::Closure | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) | Target::Impl - | Target::Mod => true, + | Target::Mod => {} _ => { self.dcx().emit_err(errors::CoverageNotFnOrClosure { attr_span: attr.span, defn_span: span, }); - false + } + } + } + + /// Checks that `#[optimize(..)]` is applied to a function/closure/method, + /// or to an impl block or module. + // FIXME(#128488): this should probably be elevated to an error? + fn check_optimize(&self, hir_id: HirId, attr: &Attribute, target: Target) { + match target { + Target::Fn + | Target::Closure + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + | Target::Impl + | Target::Mod => {} + + _ => { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OptimizeNotFnOrClosure, + ); } } } @@ -418,7 +479,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], - ) -> bool { + ) { // many attributes don't make sense in combination with #[naked]. // Notable attributes that are incompatible with `#[naked]` are: // @@ -461,6 +522,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => { for other_attr in attrs { + // this covers "sugared doc comments" of the form `/// ...` + // it does not cover `#[doc = "..."]`, which is handled below + if other_attr.is_doc_comment() { + continue; + } + if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) { self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { span: other_attr.span, @@ -468,19 +535,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: other_attr.name_or_empty(), }); - return false; + return; } } - - true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[naked]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked"); - true + self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked") } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -488,7 +552,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } @@ -500,17 +563,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { match target { Target::Fn - | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { attr_span: attr.span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } @@ -536,19 +598,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[collapse_debuginfo]` is applied to a macro. - fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::MacroDef => true, + Target::MacroDef => {} _ => { self.tcx .dcx() .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span }); - false } } } - /// Checks if a `#[track_caller]` is applied to a function. Returns `true` if valid. + /// Checks if a `#[track_caller]` is applied to a function. fn check_track_caller( &self, hir_id: HirId, @@ -556,7 +617,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attrs: &[Attribute], span: Span, target: Target, - ) -> bool { + ) { match target { Target::Fn => { // `#[track_caller]` is not valid on weak lang items because they are called via @@ -572,12 +633,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { name: lang_item, sig_span: sig.span, }); - false - } else { - true } } - Target::Method(..) | Target::ForeignFn | Target::Closure => true, + Target::Method(..) | Target::ForeignFn | Target::Closure => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[track_caller]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible @@ -586,7 +644,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller"); } - true } _ => { self.dcx().emit_err(errors::TrackedCallerWrongLocation { @@ -594,62 +651,51 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } - /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. - fn check_non_exhaustive( - &self, - hir_id: HirId, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. + fn check_non_exhaustive(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct | Target::Enum | Target::Variant => true, + Target::Struct | Target::Enum | Target::Variant => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[non_exhaustive]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive"); - true } _ => { self.dcx().emit_err(errors::NonExhaustiveWrongLocation { attr_span: attr.span, defn_span: span, }); - false } } } - /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid. - fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if the `#[marker]` attribute on an `item` is valid. + fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::Trait => true, + Target::Trait => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[marker]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker"); - true } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span: attr.span, defn_span: span, }); - false } } } - /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid. + /// Checks if the `#[target_feature]` attribute on `item` is valid. fn check_target_feature( &self, hir_id: HirId, @@ -657,7 +703,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], - ) -> bool { + ) { match target { Target::Fn => { // `#[target_feature]` is not allowed in lang items. @@ -674,12 +720,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { name: lang_item, sig_span: sig.span, }); - false - } else { - true } } - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {} // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. Target::Statement => { @@ -689,7 +732,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr.span, errors::TargetFeatureOnStatement, ); - true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[target_feature]` attribute with just a lint, because we previously @@ -697,7 +739,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); - true } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -705,21 +746,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false } } } - /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid. - fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if the `#[thread_local]` attribute on `item` is valid. + fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::ForeignStatic | Target::Static => true, + Target::ForeignStatic | Target::Static => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic { attr_span: attr.span, defn_span: span, }); - false } } } @@ -736,14 +775,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target: Target, is_list: bool, aliases: &mut FxHashMap, - ) -> bool { + ) { let tcx = self.tcx; let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); let attr_str = &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); if doc_alias == kw::Empty { tcx.dcx().emit_err(errors::DocAliasEmpty { span, attr_str }); - return false; + return; } let doc_alias_str = doc_alias.as_str(); @@ -752,11 +791,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { tcx.dcx().emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); - return false; + return; } if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { tcx.dcx().emit_err(errors::DocAliasStartEnd { span, attr_str }); - return false; + return; } let span = meta.span(); @@ -781,7 +820,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } // we check the validity of params elsewhere - Target::Param => return false, + Target::Param => return, Target::Expression | Target::Statement | Target::Arm @@ -814,12 +853,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ExprField => None, } { tcx.dcx().emit_err(errors::DocAliasBadLocation { span, attr_str, location }); - return false; + return; } let item_name = self.tcx.hir().name(hir_id); if item_name == doc_alias { tcx.dcx().emit_err(errors::DocAliasNotAnAlias { span, attr_str }); - return false; + return; } if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { self.tcx.emit_node_span_lint( @@ -829,7 +868,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, ); } - true } fn check_doc_alias( @@ -838,46 +876,39 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir_id: HirId, target: Target, aliases: &mut FxHashMap, - ) -> bool { + ) { if let Some(values) = meta.meta_item_list() { - let mut errors = 0; for v in values { match v.lit() { Some(l) => match l.kind { LitKind::Str(s, _) => { - if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) { - errors += 1; - } + self.check_doc_alias_value(v, s, hir_id, target, true, aliases); } _ => { self.tcx .dcx() .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); - errors += 1; } }, None => { self.tcx .dcx() .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); - errors += 1; } } } - errors == 0 } else if let Some(doc_alias) = meta.value_str() { self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) } else { self.dcx().emit_err(errors::DocAliasMalformed { span: meta.span() }); - false } } - fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) { let doc_keyword = meta.value_str().unwrap_or(kw::Empty); if doc_keyword == kw::Empty { self.doc_attr_str_error(meta, "keyword"); - return false; + return; } let item_kind = match self.tcx.hir_node(hir_id) { hir::Node::Item(item) => Some(&item.kind), @@ -887,12 +918,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Some(ItemKind::Mod(module)) => { if !module.item_ids.is_empty() { self.dcx().emit_err(errors::DocKeywordEmptyMod { span: meta.span() }); - return false; + return; } } _ => { self.dcx().emit_err(errors::DocKeywordNotMod { span: meta.span() }); - return false; + return; } } if !rustc_lexer::is_ident(doc_keyword.as_str()) { @@ -900,12 +931,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), doc_keyword, }); - return false; } - true } - fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { + fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) { let item_kind = match self.tcx.hir_node(hir_id) { hir::Node::Item(item) => Some(&item.kind), _ => None, @@ -920,18 +949,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }; if !is_valid { self.dcx().emit_err(errors::DocFakeVariadicNotValid { span: meta.span() }); - return false; } } _ => { self.dcx().emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); - return false; } } - true } - /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid. + /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. /// /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or /// if there are conflicting attributes for one item. @@ -947,7 +973,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir_id: HirId, target: Target, specified_inline: &mut Option<(bool, Span)>, - ) -> bool { + ) { match target { Target::Use | Target::ExternCrate => { let do_inline = meta.name_or_empty() == sym::inline; @@ -960,12 +986,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fluent::passes_doc_inline_conflict_second, ); self.dcx().emit_err(errors::DocKeywordConflict { spans }); - return false; } - true } else { *specified_inline = Some((do_inline, meta.span())); - true } } _ => { @@ -979,7 +1002,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .then(|| self.tcx.hir().span(hir_id)), }, ); - false } } } @@ -990,7 +1012,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta: &NestedMetaItem, hir_id: HirId, target: Target, - ) -> bool { + ) { if target != Target::ExternCrate { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1002,7 +1024,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .then(|| self.tcx.hir().span(hir_id)), }, ); - return false; + return; } if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() { @@ -1016,10 +1038,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { .then(|| self.tcx.hir().span(hir_id)), }, ); - return false; } - - true } /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. @@ -1064,8 +1083,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if /// valid. - fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { - let mut is_valid = true; + fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) { if let Some(metas) = meta.meta_item_list() { for i_meta in metas { match (i_meta.name_or_empty(), i_meta.meta_item()) { @@ -1079,7 +1097,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { path: rustc_ast_pretty::pprust::path_to_string(&m.path), }, ); - is_valid = false; } (_, None) => { self.tcx.emit_node_span_lint( @@ -1088,7 +1105,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { i_meta.span(), errors::DocTestLiteral, ); - is_valid = false; } } } @@ -1099,28 +1115,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocTestTakesList, ); - is_valid = false; } - is_valid } /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes. - /// Returns `true` if valid. - fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { - if meta.meta_item_list().is_some() { - true - } else { + /// + fn check_doc_cfg_hide(&self, meta: &NestedMetaItem, hir_id: HirId) { + if meta.meta_item_list().is_none() { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), errors::DocCfgHideTakesList, ); - false } } - /// Runs various checks on `#[doc]` attributes. Returns `true` if valid. + /// Runs various checks on `#[doc]` attributes. /// /// `specified_inline` should be initialized to `None` and kept for the scope /// of one item. Read the documentation of [`check_doc_inline`] for more information. @@ -1134,34 +1145,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target: Target, specified_inline: &mut Option<(bool, Span)>, aliases: &mut FxHashMap, - ) -> bool { - let mut is_valid = true; - + ) { if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() { for meta in list { if let Some(i_meta) = meta.meta_item() { match i_meta.name_or_empty() { - sym::alias - if !self.check_attr_not_crate_level(meta, hir_id, "alias") - || !self.check_doc_alias(meta, hir_id, target, aliases) => - { - is_valid = false + sym::alias => { + if self.check_attr_not_crate_level(meta, hir_id, "alias") { + self.check_doc_alias(meta, hir_id, target, aliases); + } } - sym::keyword - if !self.check_attr_not_crate_level(meta, hir_id, "keyword") - || !self.check_doc_keyword(meta, hir_id) => - { - is_valid = false + sym::keyword => { + if self.check_attr_not_crate_level(meta, hir_id, "keyword") { + self.check_doc_keyword(meta, hir_id); + } } - sym::fake_variadic - if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") - || !self.check_doc_fake_variadic(meta, hir_id) => - { - is_valid = false + sym::fake_variadic => { + if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { + self.check_doc_fake_variadic(meta, hir_id); + } + } + + sym::test => { + if self.check_attr_crate_level(attr, meta, hir_id) { + self.check_test_attr(meta, hir_id); + } } sym::html_favicon_url @@ -1169,62 +1181,36 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::html_playground_url | sym::issue_tracker_base_url | sym::html_root_url - | sym::html_no_source - | sym::test - | sym::rust_logo - if !self.check_attr_crate_level(attr, meta, hir_id) => - { - is_valid = false; + | sym::html_no_source => { + self.check_attr_crate_level(attr, meta, hir_id); } - sym::cfg_hide - if !self.check_attr_crate_level(attr, meta, hir_id) - || !self.check_doc_cfg_hide(meta, hir_id) => - { - is_valid = false; + sym::cfg_hide => { + if self.check_attr_crate_level(attr, meta, hir_id) { + self.check_doc_cfg_hide(meta, hir_id); + } } - sym::inline | sym::no_inline - if !self.check_doc_inline( - attr, - meta, - hir_id, - target, - specified_inline, - ) => - { - is_valid = false; + sym::inline | sym::no_inline => { + self.check_doc_inline(attr, meta, hir_id, target, specified_inline) } - sym::masked if !self.check_doc_masked(attr, meta, hir_id, target) => { - is_valid = false; - } + sym::masked => self.check_doc_masked(attr, meta, hir_id, target), // no_default_passes: deprecated // passes: deprecated // plugins: removed, but rustdoc warns about it itself - sym::alias - | sym::cfg - | sym::cfg_hide + sym::cfg | sym::hidden - | sym::html_favicon_url - | sym::html_logo_url - | sym::html_no_source - | sym::html_playground_url - | sym::html_root_url - | sym::inline - | sym::issue_tracker_base_url - | sym::keyword - | sym::masked | sym::no_default_passes - | sym::no_inline | sym::notable_trait | sym::passes - | sym::plugins - | sym::fake_variadic => {} + | sym::plugins => {} sym::rust_logo => { - if !self.tcx.features().rustdoc_internals { + if self.check_attr_crate_level(attr, meta, hir_id) + && !self.tcx.features().rustdoc_internals + { feature_err( &self.tcx.sess, sym::rustdoc_internals, @@ -1235,12 +1221,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - sym::test => { - if !self.check_test_attr(meta, hir_id) { - is_valid = false; - } - } - _ => { let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path); if i_meta.has_name(sym::spotlight) { @@ -1282,7 +1262,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocTestUnknownAny { path }, ); } - is_valid = false; } } } else { @@ -1292,79 +1271,60 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocInvalid, ); - is_valid = false; } } } - - is_valid } /// Warns against some misuses of `#[pass_by_value]` - fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct | Target::Enum | Target::TyAlias => true, + Target::Struct | Target::Enum | Target::TyAlias => {} _ => { self.dcx().emit_err(errors::PassByValue { attr_span: attr.span, span }); - false } } } - fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Method(MethodKind::Inherent) => true, + Target::Method(MethodKind::Inherent) => {} _ => { self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); - false } } } - fn check_has_incoherent_inherent_impls( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => { - true - } + Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {} _ => { self.tcx .dcx() .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); - false } } } - fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) -> bool { + fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute], target: Target) { if target != Target::ForeignFn { self.dcx().emit_err(errors::FfiPureInvalidTarget { attr_span }); - return false; + return; } if attrs.iter().any(|a| a.has_name(sym::ffi_const)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` self.dcx().emit_err(errors::BothFfiConstAndPure { attr_span }); - false - } else { - true } } - fn check_ffi_const(&self, attr_span: Span, target: Target) -> bool { - if target == Target::ForeignFn { - true - } else { + fn check_ffi_const(&self, attr_span: Span, target: Target) { + if target != Target::ForeignFn { self.dcx().emit_err(errors::FfiConstInvalidTarget { attr_span }); - false } } /// Warns against some misuses of `#[must_use]` - fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) -> bool { + fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { if !matches!( target, Target::Fn @@ -1397,23 +1357,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::MustUseNoEffect { article, target }, ); } - - // For now, its always valid - true } - /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid. - fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if `#[must_not_suspend]` is applied to a function. + fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct | Target::Enum | Target::Union | Target::Trait => true, + Target::Struct | Target::Enum | Target::Union | Target::Trait => {} _ => { self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); - false } } } - /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid. + /// Checks if `#[cold]` is applied to a non-function. fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} @@ -1489,21 +1445,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid. - fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { + /// Checks if `#[no_link]` is applied to an `extern crate`. + fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::ExternCrate => true, + Target::ExternCrate => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[no_link]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); - true } _ => { self.dcx().emit_err(errors::NoLink { attr_span: attr.span, span }); - false } } } @@ -1512,57 +1466,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> { matches!(self.tcx.hir_node(hir_id), hir::Node::ImplItem(..)) } - /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid. - fn check_export_name( - &self, - hir_id: HirId, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + /// Checks if `#[export_name]` is applied to a function or static. + fn check_export_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { match target { - Target::Static | Target::Fn => true, - Target::Method(..) if self.is_impl_item(hir_id) => true, + Target::Static | Target::Fn => {} + Target::Method(..) if self.is_impl_item(hir_id) => {} // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[export_name]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); - true } _ => { self.dcx().emit_err(errors::ExportName { attr_span: attr.span, span }); - false } } } - fn check_rustc_layout_scalar_valid_range( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_rustc_layout_scalar_valid_range(&self, attr: &Attribute, span: Span, target: Target) { if target != Target::Struct { self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct { attr_span: attr.span, span, }); - return false; + return; } let Some(list) = attr.meta_item_list() else { - return false; + return; }; - if matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) { - true - } else { + if !matches!(&list[..], &[NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) + { self.tcx .dcx() .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); - false } } @@ -1574,7 +1513,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, item: Option>, - ) -> bool { + ) { let is_function = matches!(target, Target::Fn); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -1582,12 +1521,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - return false; + return; } let Some(list) = attr.meta_item_list() else { // The attribute form is validated on AST. - return false; + return; }; let Some(ItemLike::Item(Item { @@ -1605,7 +1544,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span: attr.span, param_span: param.span, }); - return false; + return; } } } @@ -1615,7 +1554,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span: attr.span, generics_span: generics.span, }); - return false; + return; } let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128; @@ -1628,7 +1567,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span, arg_count: arg_count as usize, }); - return false; + return; } } else { invalid_args.push(meta.span()); @@ -1637,9 +1576,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if !invalid_args.is_empty() { self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args }); - false - } else { - true } } @@ -1651,7 +1587,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { @@ -1659,9 +1595,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); - false - } else { - true } } @@ -1673,7 +1606,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } @@ -1685,60 +1618,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. - fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Struct => true, + Target::Struct => {} _ => { self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); - false } } } /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. - fn check_rustc_lint_opt_deny_field_access( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_rustc_lint_opt_deny_field_access(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Field => true, + Target::Field => {} _ => { self.tcx .dcx() .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); - false } } } /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. - fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { - if self.tcx.sess.opts.unstable_opts.query_dep_graph { - true - } else { + fn check_rustc_dirty_clean(&self, attr: &Attribute) { + if !self.tcx.sess.opts.unstable_opts.query_dep_graph { self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span }); - false } } /// Checks if the attribute is applied to a trait. - fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) -> bool { + fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Trait => true, + Target::Trait => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span: attr.span, defn_span: span, }); - false } } } @@ -2036,43 +1958,38 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: Span, target: Target, attrs: &[Attribute], - ) -> bool { + ) { debug!("Checking target: {:?}", target); match target { Target::Fn => { for attr in attrs { if attr.is_proc_macro_attr() { debug!("Is proc macro attr"); - return true; + return; } } debug!("Is not proc macro attr"); - false } - Target::MacroDef => true, + Target::MacroDef => {} // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. - Target::Field | Target::Arm => { - self.inline_attr_str_error_without_macro_def( - hir_id, - attr, - "allow_internal_unstable", - ); - true - } + Target::Field | Target::Arm => self.inline_attr_str_error_without_macro_def( + hir_id, + attr, + "allow_internal_unstable", + ), _ => { self.tcx .dcx() .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); - false } } } /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. - fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool { + fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) { // Here we only check that the #[debugger_visualizer] attribute is attached // to nothing other than a module. All other checks are done in the // `debugger_visualizer` query where they need to be done for decoding @@ -2081,11 +1998,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Mod => {} _ => { self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span }); - return false; } } - - true } /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. @@ -2096,26 +2010,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { match target { Target::Fn | Target::Method(_) - if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) => - { - true - } + if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) => {} // FIXME(#80564): We permit struct fields and match arms to have an // `#[allow_internal_unstable]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable"); - true + self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable") } _ => { self.tcx .dcx() .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); - false } } } @@ -2126,65 +2035,56 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, span: Span, target: Target, - ) -> bool { + ) { if let Target::ForeignFn = target && let hir::Node::Item(Item { kind: ItemKind::ForeignMod { abi: Abi::RustIntrinsic, .. }, .. }) = self.tcx.parent_hir_node(hir_id) { - return true; + return; } self.dcx().emit_err(errors::RustcSafeIntrinsic { attr_span: attr.span, span }); - false } - fn check_rustc_std_internal_symbol( - &self, - attr: &Attribute, - span: Span, - target: Target, - ) -> bool { + fn check_rustc_std_internal_symbol(&self, attr: &Attribute, span: Span, target: Target) { match target { - Target::Fn | Target::Static => true, + Target::Fn | Target::Static => {} _ => { self.tcx .dcx() .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); - false } } } - fn check_stability_promotable(&self, attr: &Attribute, target: Target) -> bool { + fn check_stability_promotable(&self, attr: &Attribute, target: Target) { match target { Target::Expression => { self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span }); - false } - _ => true, + _ => {} } } - fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool { + fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) { match target { - Target::ForeignFn | Target::ForeignStatic => true, + Target::ForeignFn | Target::ForeignStatic => {} _ => { self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span }); - false } } } - fn check_confusables(&self, attr: &Attribute, target: Target) -> bool { + fn check_confusables(&self, attr: &Attribute, target: Target) { match target { Target::Method(MethodKind::Inherent) => { let Some(meta) = attr.meta() else { - return false; + return; }; let ast::MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { - return false; + return; }; let mut candidates = Vec::new(); @@ -2198,21 +2098,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hi: meta.span().shrink_to_hi(), }, }); - return false; + return; }; candidates.push(meta_lit.symbol); } if candidates.is_empty() { self.dcx().emit_err(errors::EmptyConfusables { span: attr.span }); - return false; } - - true } _ => { self.dcx().emit_err(errors::Confusables { attr_span: attr.span }); - false } } } @@ -2439,6 +2335,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.abort.set(true); } } + + fn check_coroutine(&self, attr: &Attribute, target: Target) { + match target { + Target::Closure => return, + _ => { + self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span }); + } + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 8a931fc4158e3..7ae5c9040042c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -17,7 +17,7 @@ use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -44,79 +44,16 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -struct Publicness { - ty_is_public: bool, - ty_and_all_fields_are_public: bool, -} - -impl Publicness { - fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self { - Self { ty_is_public, ty_and_all_fields_are_public } - } -} - -fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { - match ty.kind { - TyKind::Path(hir::QPath::Resolved(_, path)) => { - if let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() - { - Some((local_def_id, def_kind)) - } else { - None - } - } - TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), - TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), - _ => None, - } -} - -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { - let adt_def = tcx.adt_def(id); - - // skip types contain fields of unit and never type, - // it's usually intentional to make the type not constructible - let not_require_constructor = adt_def.all_fields().any(|field| { - let field_type = tcx.type_of(field.did).instantiate_identity(); - field_type.is_unit() || field_type.is_never() - }); - - not_require_constructor - || adt_def.all_fields().all(|field| { - let field_type = tcx.type_of(field.did).instantiate_identity(); - // skip fields of PhantomData, - // cause it's a common way to check things like well-formedness - if field_type.is_phantom_data() { - return true; - } - - field.vis.is_public() - }) -} - -/// check struct and its fields are public or not, -/// for enum and union, just check they are public, -/// and doesn't solve types like &T for now, just skip them -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { - if let Some((def_id, def_kind)) = adt_of(ty) { - return match def_kind { - DefKind::Enum | DefKind::Union => { - let ty_is_public = tcx.visibility(def_id).is_public(); - Publicness::new(ty_is_public, ty_is_public) - } - DefKind::Struct => { - let ty_is_public = tcx.visibility(def_id).is_public(); - Publicness::new( - ty_is_public, - ty_is_public && struct_all_fields_are_public(tcx, def_id), - ) - } - _ => Publicness::new(true, true), - }; +fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { + if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let Res::Def(def_kind, def_id) = path.res + && def_id.is_local() + && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) + { + tcx.visibility(def_id).is_public() + } else { + true } - - Publicness::new(true, true) } /// Determine if a work from the worklist is coming from a `#[allow]` @@ -172,10 +109,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn handle_res(&mut self, res: Res) { match res { - Res::Def( - DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias, - def_id, - ) => { + Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => { self.check_def_id(def_id); } _ if self.in_pat => {} @@ -294,10 +228,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { pats: &[hir::PatField<'_>], ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => { - self.check_def_id(adt.did()); - adt.variant_of_res(res) - } + ty::Adt(adt, _) => adt.variant_of_res(res), _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; for pat in pats { @@ -317,10 +248,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => { - self.check_def_id(adt.did()); - adt.variant_of_res(res) - } + ty::Adt(adt, _) => adt.variant_of_res(res), _ => { self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); return; @@ -425,6 +353,31 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { return false; } + // don't ignore impls for Enums and pub Structs whose methods don't have self receiver, + // cause external crate may call such methods to construct values of these types + if let Some(local_impl_of) = impl_of.as_local() + && let Some(local_def_id) = def_id.as_local() + && let Some(fn_sig) = + self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) + && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) + && let TyKind::Path(hir::QPath::Resolved(_, path)) = + self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind + && let Res::Def(def_kind, did) = path.res + { + match def_kind { + // for example, #[derive(Default)] pub struct T(i32); + // external crate can call T::default() to construct T, + // so that don't ignore impl Default for pub Enum and Structs + DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => { + return false; + } + // don't ignore impl Default for Enums, + // cause we don't know which variant is constructed + DefKind::Enum => return false, + _ => (), + }; + } + if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { @@ -467,7 +420,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => { + hir::ItemKind::Trait(..) => { for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) { if let Some(local_def_id) = impl_def_id.as_local() && let ItemKind::Impl(impl_ref) = @@ -480,12 +433,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); } } - // mark assoc ty live if the trait is live - for trait_item in trait_item_refs { - if let hir::AssocItemKind::Type = trait_item.kind { - self.check_def_id(trait_item.id.owner_id.to_def_id()); - } - } + intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), @@ -502,12 +450,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let ItemKind::Impl(impl_ref) = self.tcx.hir().expect_item(local_impl_id).kind { - if !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - .ty_and_all_fields_are_public + if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) + && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) { - // skip impl-items of non pure pub ty, - // cause we don't know the ty is constructed or not, - // check these later in `solve_rest_impl_items` + // skip methods of private ty, + // they would be solved in `solve_rest_impl_items` continue; } @@ -582,25 +529,28 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let Some((local_def_id, def_kind)) = - adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty) + if let TyKind::Path(hir::QPath::Resolved(_, path)) = + self.tcx.hir().item(impl_id).expect_impl().self_ty.kind + && let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { + if self.tcx.visibility(impl_item_id).is_public() { + // for the public method, we don't know the trait item is used or not, + // so we mark the method live if the self is used + return self.live_symbols.contains(&local_def_id); + } + if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id && let Some(local_id) = trait_item_id.as_local() { - // for the local impl item, we can know the trait item is used or not, + // for the private method, we can know the trait item is used or not, // so we mark the method live if the self is used and the trait item is used - self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id) - } else { - // for the foreign method and inherent pub method, - // we don't know the trait item or the method is used or not, - // so we mark the method live if the self is used - self.live_symbols.contains(&local_def_id) + return self.live_symbols.contains(&local_id) + && self.live_symbols.contains(&local_def_id); } - } else { - false } + false } } @@ -686,9 +636,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.handle_field_pattern_match(pat, res, fields); } PatKind::Path(ref qpath) => { - if let ty::Adt(adt, _) = self.typeck_results().node_type(pat.hir_id).kind() { - self.check_def_id(adt.did()); - } let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_res(res); } @@ -825,9 +772,7 @@ fn check_item<'tcx>( .iter() .filter_map(|def_id| def_id.as_local()); - let self_ty = tcx.hir().item(id).expect_impl().self_ty; - let Publicness { ty_is_public, ty_and_all_fields_are_public } = - ty_ref_to_pub_struct(tcx, self_ty); + let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty); // And we access the Map here to get HirId from LocalDefId for local_def_id in local_def_ids { @@ -843,19 +788,18 @@ fn check_item<'tcx>( // for trait impl blocks, // mark the method live if the self_ty is public, // or the method is public and may construct self - if tcx.visibility(local_def_id).is_public() - && (ty_and_all_fields_are_public || (ty_is_public && may_construct_self)) + if of_trait + && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) + || tcx.visibility(local_def_id).is_public() + && (ty_is_pub || may_construct_self)) { - // if the impl item is public, - // and the ty may be constructed or can be constructed in foreign crates, - // mark the impl item live worklist.push((local_def_id, ComesFromAllowExpect::No)); } else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); - } else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public { - // private impl items of traits || public impl items not constructs self + } else if of_trait { + // private method || public method not constructs self unsolved_impl_items.push((id, local_def_id)); } } @@ -881,13 +825,10 @@ fn check_trait_item( worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, id: hir::TraitItemId, ) { - use hir::TraitItemKind::{Const, Fn, Type}; - if matches!( - tcx.def_kind(id.owner_id), - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn - ) { + use hir::TraitItemKind::{Const, Fn}; + if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) { let trait_item = tcx.hir().trait_item(id); - if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..)) + if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..)) && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id) { @@ -925,14 +866,6 @@ fn create_and_seed_worklist( effective_vis .is_public_at_level(Level::Reachable) .then_some(id) - .filter(|&id| - // checks impls, impl-items and pub structs with all public fields later - match tcx.def_kind(id) { - DefKind::Impl { .. } => false, - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), - _ => true - }) .map(|id| (id, ComesFromAllowExpect::No)) }) // Seed entry point @@ -1216,7 +1149,6 @@ impl<'tcx> DeadVisitor<'tcx> { } match self.tcx.def_kind(def_id) { DefKind::AssocConst - | DefKind::AssocTy | DefKind::AssocFn | DefKind::Fn | DefKind::Static { .. } @@ -1258,14 +1190,10 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused assocs in traits + // We have diagnosed unused methods in traits if matches!(def_kind, DefKind::Impl { of_trait: true }) - && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn) - // skip unused public inherent methods, - // cause we have diagnosed unconstructed struct - || matches!(def_kind, DefKind::Impl { of_trait: false }) - && tcx.visibility(def_id).is_public() - && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public + && tcx.def_kind(def_id) == DefKind::AssocFn + || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn { continue; } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index ff85be109d460..36dfc40e7628b 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -68,6 +68,10 @@ pub struct CoverageNotFnOrClosure { pub defn_span: Span, } +#[derive(LintDiagnostic)] +#[diag(passes_optimize_not_fn_or_closure)] +pub struct OptimizeNotFnOrClosure; + #[derive(Diagnostic)] #[diag(passes_should_be_applied_to_fn)] pub struct AttrShouldBeAppliedToFn { @@ -632,6 +636,13 @@ pub struct Confusables { pub attr_span: Span, } +#[derive(Diagnostic)] +#[diag(passes_coroutine_on_non_closure)] +pub struct CoroutineOnNonClosure { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_empty_confusables)] pub(crate) struct EmptyConfusables { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 126e5357cd8b9..6290aeb252312 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -7,7 +7,7 @@ use rustc_hir::HirId; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::{self, Const}; -use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; +use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef, @@ -26,6 +26,8 @@ use crate::pat_column::PatternColumn; use crate::usefulness::{compute_match_usefulness, PlaceValidity}; use crate::{errors, Captures, PatCx, PrivateUninhabitedField}; +mod print; + // Re-export rustc-specific versions of all these types. pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; @@ -773,8 +775,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } - /// Convert back to a `thir::Pat` for diagnostic purposes. - fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> { + /// Convert to a [`print::Pat`] for diagnostic purposes. + fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<'tcx> { + use print::{Pat, PatKind}; use MaybeInfiniteInt::*; let cx = self; let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) { @@ -808,19 +811,20 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() })) }; - Pat { ty: ty.inner(), span: DUMMY_SP, kind } + Pat { ty: ty.inner(), kind } } /// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes. pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String { - // This works by converting the witness pattern back to a `thir::Pat` + // This works by converting the witness pattern to a `print::Pat` // and then printing that, but callers don't need to know that. self.hoist_witness_pat(pat).to_string() } - /// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't + /// Convert to a [`print::Pat`] for diagnostic purposes. This panics for patterns that don't /// appear in diagnostics, like float ranges. - fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> { + fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { + use print::{FieldPat, Pat, PatKind}; let cx = self; let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild); let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p))); @@ -840,7 +844,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // the pattern is a box pattern. PatKind::Deref { subpattern: subpatterns.next().unwrap() } } - ty::Adt(adt_def, args) => { + ty::Adt(adt_def, _args) => { let variant_index = RustcPatCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); let subpatterns = subpatterns .enumerate() @@ -848,7 +852,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { .collect(); if adt_def.is_enum() { - PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns } + PatKind::Variant { adt_def: *adt_def, variant_index, subpatterns } } else { PatKind::Leaf { subpatterns } } @@ -885,7 +889,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } let suffix: Box<[_]> = subpatterns.collect(); - let wild = Pat::wildcard_from_ty(pat.ty().inner()); + let wild = Pat { ty: pat.ty().inner(), kind: PatKind::Wild }; PatKind::Slice { prefix: prefix.into_boxed_slice(), slice: Some(Box::new(wild)), @@ -906,7 +910,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } }; - Pat { ty: pat.ty().inner(), span: DUMMY_SP, kind } + Pat { ty: pat.ty().inner(), kind } } } @@ -1003,12 +1007,11 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { } // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with // `gap+1`. - let suggested_range: thir::Pat<'_> = { + let suggested_range: String = { // Suggest `lo..=gap` instead. - let mut suggested_range = thir_pat.clone(); - let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() }; - range.end = rustc_hir::RangeEnd::Included; - suggested_range + let mut suggested_range = PatRange::clone(range); + suggested_range.end = rustc_hir::RangeEnd::Included; + suggested_range.to_string() }; let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty()); if gapped_with.is_empty() { @@ -1023,7 +1026,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { // That's the gap that isn't covered. max: gap_as_pat.to_string(), // Suggest `lo..=max` instead. - suggestion: suggested_range.to_string(), + suggestion: suggested_range, }, ); } else { @@ -1037,7 +1040,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { // That's the gap that isn't covered. gap: gap_as_pat.to_string(), // Suggest `lo..=gap` instead. - suggestion: suggested_range.to_string(), + suggestion: suggested_range, // All these ranges skipped over `gap` which we think is probably a // mistake. gap_with: gapped_with @@ -1045,7 +1048,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { .map(|pat| errors::GappedRange { span: pat.data().span, gap: gap_as_pat.to_string(), - first_range: thir_pat.to_string(), + first_range: range.to_string(), }) .collect(), }, diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs new file mode 100644 index 0000000000000..4b76764e8b136 --- /dev/null +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -0,0 +1,193 @@ +//! Pattern analysis sometimes wants to print patterns as part of a user-visible +//! diagnostic. +//! +//! Historically it did so by creating a synthetic [`thir::Pat`](rustc_middle::thir::Pat) +//! and printing that, but doing so was making it hard to modify the THIR pattern +//! representation for other purposes. +//! +//! So this module contains a forked copy of `thir::Pat` that is used _only_ +//! for diagnostics, and has been partly simplified to remove things that aren't +//! needed for printing. + +use std::fmt; + +use rustc_middle::thir::PatRange; +use rustc_middle::ty::{self, AdtDef, Ty}; +use rustc_middle::{bug, mir}; +use rustc_span::sym; +use rustc_target::abi::{FieldIdx, VariantIdx}; + +#[derive(Clone, Debug)] +pub(crate) struct FieldPat<'tcx> { + pub(crate) field: FieldIdx, + pub(crate) pattern: Box>, +} + +#[derive(Clone, Debug)] +pub(crate) struct Pat<'tcx> { + pub(crate) ty: Ty<'tcx>, + pub(crate) kind: PatKind<'tcx>, +} + +#[derive(Clone, Debug)] +pub(crate) enum PatKind<'tcx> { + Wild, + + Variant { + adt_def: AdtDef<'tcx>, + variant_index: VariantIdx, + subpatterns: Vec>, + }, + + Leaf { + subpatterns: Vec>, + }, + + Deref { + subpattern: Box>, + }, + + Constant { + value: mir::Const<'tcx>, + }, + + Range(Box>), + + Slice { + prefix: Box<[Box>]>, + slice: Option>>, + suffix: Box<[Box>]>, + }, + + Never, +} + +impl<'tcx> fmt::Display for Pat<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Printing lists is a chore. + let mut first = true; + let mut start_or_continue = |s| { + if first { + first = false; + "" + } else { + s + } + }; + let mut start_or_comma = || start_or_continue(", "); + + match self.kind { + PatKind::Wild => write!(f, "_"), + PatKind::Never => write!(f, "!"), + PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { + let variant_and_name = match self.kind { + PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| { + let variant = adt_def.variant(variant_index); + let adt_did = adt_def.did(); + let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did) + || tcx.get_diagnostic_item(sym::Result) == Some(adt_did) + { + variant.name.to_string() + } else { + format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) + }; + Some((variant, name)) + }), + _ => self.ty.ty_adt_def().and_then(|adt_def| { + if !adt_def.is_enum() { + ty::tls::with(|tcx| { + Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did()))) + }) + } else { + None + } + }), + }; + + if let Some((variant, name)) = &variant_and_name { + write!(f, "{name}")?; + + // Only for Adt we can have `S {...}`, + // which we handle separately here. + if variant.ctor.is_none() { + write!(f, " {{ ")?; + + let mut printed = 0; + for p in subpatterns { + if let PatKind::Wild = p.pattern.kind { + continue; + } + let name = variant.fields[p.field].name; + write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; + printed += 1; + } + + let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); + if printed < variant.fields.len() && (!is_union || printed == 0) { + write!(f, "{}..", start_or_comma())?; + } + + return write!(f, " }}"); + } + } + + let num_fields = + variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); + if num_fields != 0 || variant_and_name.is_none() { + write!(f, "(")?; + for i in 0..num_fields { + write!(f, "{}", start_or_comma())?; + + // Common case: the field is where we expect it. + if let Some(p) = subpatterns.get(i) { + if p.field.index() == i { + write!(f, "{}", p.pattern)?; + continue; + } + } + + // Otherwise, we have to go looking for it. + if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { + write!(f, "{}", p.pattern)?; + } else { + write!(f, "_")?; + } + } + write!(f, ")")?; + } + + Ok(()) + } + PatKind::Deref { ref subpattern } => { + match self.ty.kind() { + ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, + ty::Ref(_, _, mutbl) => { + write!(f, "&{}", mutbl.prefix_str())?; + } + _ => bug!("{} is a bad Deref pattern type", self.ty), + } + write!(f, "{subpattern}") + } + PatKind::Constant { value } => write!(f, "{value}"), + PatKind::Range(ref range) => write!(f, "{range}"), + PatKind::Slice { ref prefix, ref slice, ref suffix } => { + write!(f, "[")?; + for p in prefix.iter() { + write!(f, "{}{}", start_or_comma(), p)?; + } + if let Some(ref slice) = *slice { + write!(f, "{}", start_or_comma())?; + match slice.kind { + PatKind::Wild => {} + _ => write!(f, "{slice}")?, + } + write!(f, "..")?; + } + for p in suffix.iter() { + write!(f, "{}{}", start_or_comma(), p)?; + } + write!(f, "]") + } + } + } +} diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 761d6acdbaeb9..ca3efc11201e1 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -588,7 +588,7 @@ pub fn report_cycle<'a>( cycle_stack, stack_bottom: stack[0].query.description.to_owned(), alias, - cycle_usage: cycle_usage, + cycle_usage, stack_count, note_span: (), }; diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ccb7223b62128..8080bb60e415e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2026,14 +2026,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Applicability::MaybeIncorrect, )), ) + } else if ident.name == kw::Underscore { + (format!("`_` is not a valid crate or module name"), None) } else if self.tcx.sess.is_rust_2015() { ( format!("you might be missing crate `{ident}`"), Some(( - vec![], - format!( - "consider adding `extern crate {ident}` to use the `{ident}` crate" - ), + vec![( + self.current_crate_outer_attr_insert_span, + format!("extern crate {ident};\n"), + )], + format!("consider importing the `{ident}` crate"), Applicability::MaybeIncorrect, )), ) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f76fa62a0094f..c7af21027b8d3 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -14,7 +14,7 @@ use rustc_middle::metadata::{ModChild, Reexport}; use rustc_middle::{span_bug, ty}; use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, - UNUSED_IMPORTS, + REDUNDANT_IMPORTS, UNUSED_IMPORTS, }; use rustc_session::lint::BuiltinLintDiag; use rustc_span::edit_distance::find_best_match_for_name; @@ -48,6 +48,7 @@ pub(crate) enum ImportKind<'a> { /// `source` in `use prefix::source as target`. source: Ident, /// `target` in `use prefix::source as target`. + /// It will directly use `source` when the format is `use prefix::source`. target: Ident, /// Bindings to which `source` refers to. source_bindings: PerNS, Determinacy>>>, @@ -1387,14 +1388,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut redundant_spans: Vec<_> = redundant_span.present_items().collect(); redundant_spans.sort(); redundant_spans.dedup(); - /* FIXME(unused_imports): Add this back as a new lint - self.lint_buffer.buffer_lint_with_diagnostic( - UNUSED_IMPORTS, + self.lint_buffer.buffer_lint( + REDUNDANT_IMPORTS, id, import.span, BuiltinLintDiag::RedundantImport(redundant_spans, source), ); - */ return true; } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 7e5fd82b80cee..f844930ad265e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2671,17 +2671,17 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Store all seen lifetimes names from outer scopes. let mut seen_lifetimes = FxHashSet::default(); - // We also can't shadow bindings from the parent item - if let RibKind::AssocItem = kind { - let mut add_bindings_for_ns = |ns| { - let parent_rib = self.ribs[ns] - .iter() - .rfind(|r| matches!(r.kind, RibKind::Item(..))) - .expect("associated item outside of an item"); + // We also can't shadow bindings from associated parent items. + for ns in [ValueNS, TypeNS] { + for parent_rib in self.ribs[ns].iter().rev() { seen_bindings.extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); - }; - add_bindings_for_ns(ValueNS); - add_bindings_for_ns(TypeNS); + + // Break at mod level, to account for nested items which are + // allowed to shadow generic param names. + if matches!(parent_rib.kind, RibKind::Module(..)) { + break; + } + } } // Forbid shadowing lifetime bindings diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 6aca0545e64de..79d3bb685b384 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -178,8 +178,8 @@ enum ImplTraitContext { /// Used for tracking import use types which will be used for redundant import checking. /// ### Used::Scope Example -/// ```rust,ignore (redundant_imports) -/// #![deny(unused_imports)] +/// ```rust,compile_fail +/// #![deny(redundant_imports)] /// use std::mem::drop; /// fn main() { /// let s = Box::new(32); @@ -1180,6 +1180,10 @@ pub struct Resolver<'a, 'tcx> { /// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations. /// Needed because glob delegations exclude explicitly defined names. impl_binding_keys: FxHashMap>, + + /// This is the `Span` where an `extern crate foo;` suggestion would be inserted, if `foo` + /// could be a crate that wasn't imported. For diagnostics use only. + current_crate_outer_attr_insert_span: Span, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1342,6 +1346,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { tcx: TyCtxt<'tcx>, attrs: &[ast::Attribute], crate_span: Span, + current_crate_outer_attr_insert_span: Span, arenas: &'a ResolverArenas<'a>, ) -> Resolver<'a, 'tcx> { let root_def_id = CRATE_DEF_ID.to_def_id(); @@ -1525,6 +1530,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { glob_delegation_invoc_ids: Default::default(), impl_unexpanded_invocations: Default::default(), impl_binding_keys: Default::default(), + current_crate_outer_attr_insert_span, }; let root_parent_scope = ParentScope::module(graph_root, &resolver); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs index ac664c53f4454..cb15c67b895b8 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use tracing::instrument; @@ -112,11 +112,12 @@ pub fn typeid_for_instance<'tcx>( instance: Instance<'tcx>, options: TypeIdOptions, ) -> String { + assert!(!instance.has_non_region_param(), "{instance:#?} must be fully monomorphic"); let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits())); let instance = transform_instance(tcx, instance, transform_ty_options); let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) + .fn_abi_of_instance(ty::ParamEnv::reveal_all().and((instance, ty::List::empty()))) .unwrap_or_else(|error| { bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") }); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index f58a991a616da..a60af3f2d7137 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -838,10 +838,14 @@ pub enum Input { impl Input { pub fn filestem(&self) -> &str { - match *self { - Input::File(ref ifile) => ifile.file_stem().unwrap().to_str().unwrap(), - Input::Str { .. } => "rust_out", + if let Input::File(ifile) = self { + // If for some reason getting the file stem as a UTF-8 string fails, + // then fallback to a fixed name. + if let Some(name) = ifile.file_stem().and_then(OsStr::to_str) { + return name; + } } + "rust_out" } pub fn source_name(&self) -> FileName { diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 0c3ec36fed5a7..63ca5fefd9faf 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -19,6 +19,11 @@ pub struct FileSearch<'a> { } impl<'a> FileSearch<'a> { + pub fn cli_search_paths(&self) -> impl Iterator { + let kind = self.kind; + self.cli_search_paths.iter().filter(move |sp| sp.kind.matches(kind)) + } + pub fn search_paths(&self) -> impl Iterator { let kind = self.kind; self.cli_search_paths diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e8563f50158a5..32fca6733bb55 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -276,6 +276,7 @@ symbols! { Path, PathBuf, Pending, + PinCoerceUnsized, Pointer, Poll, ProcMacro, @@ -1216,6 +1217,7 @@ symbols! { mir_static_mut, mir_storage_dead, mir_storage_live, + mir_tail_call, mir_unreachable, mir_unwind_cleanup, mir_unwind_continue, @@ -1705,6 +1707,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + sha512_sm_x86, shadow_call_stack, shl, shl_assign, diff --git a/compiler/rustc_target/src/abi/call/x86_win64.rs b/compiler/rustc_target/src/abi/call/x86_win64.rs index 90de1a42bc06b..4e19460bd28c2 100644 --- a/compiler/rustc_target/src/abi/call/x86_win64.rs +++ b/compiler/rustc_target/src/abi/call/x86_win64.rs @@ -1,5 +1,5 @@ use crate::abi::call::{ArgAbi, FnAbi, Reg}; -use crate::abi::Abi; +use crate::abi::{Abi, Float, Primitive}; // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing @@ -18,8 +18,12 @@ pub fn compute_abi_info(fn_abi: &mut FnAbi<'_, Ty>) { // FIXME(eddyb) there should be a size cap here // (probably what clang calls "illegal vectors"). } - Abi::Scalar(_) => { - if a.layout.size.bytes() > 8 { + Abi::Scalar(scalar) => { + // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up + // with what LLVM expects. + if a.layout.size.bytes() > 8 + && !matches!(scalar.primitive(), Primitive::Float(Float::F128)) + { a.make_indirect(); } else { a.extend_integer_width_to(32); diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs index 19b04607f0e0b..f7a6e0bd857f5 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -4,10 +4,10 @@ pub fn target() -> Target { Target { llvm_target: "loongarch64-unknown-linux-musl".into(), metadata: crate::spec::TargetMetadata { - description: Some("LoongArch64 Linux (LP64D ABI) with musl 1.2.3".into()), - tier: Some(3), - host_tools: Some(false), - std: None, // ? + description: Some("LoongArch64 Linux (LP64D ABI) with musl 1.2.5".into()), + tier: Some(2), + host_tools: Some(true), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs index 138491972e583..c7f47da6972e7 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { Target { llvm_target: "loongarch64-unknown-none".into(), metadata: crate::spec::TargetMetadata { - description: None, + description: Some("Freestanding/bare-metal LoongArch64".into()), tier: Some(2), host_tools: Some(false), std: Some(false), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs index 41519078f8074..21e4f4a2b057b 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { Target { llvm_target: "loongarch64-unknown-none".into(), metadata: crate::spec::TargetMetadata { - description: None, + description: Some("Freestanding/bare-metal LoongArch64 softfloat".into()), tier: Some(2), host_tools: Some(false), std: Some(false), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 3e575fdd528de..8b40132986867 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -21,6 +21,7 @@ pub fn target() -> Target { llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + crt_static_default: false, ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 532507cb18244..4e2617c467949 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -238,6 +238,9 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ ("rdseed", Stable), ("rtm", Unstable(sym::rtm_target_feature)), ("sha", Stable), + ("sha512", Unstable(sym::sha512_sm_x86)), + ("sm3", Unstable(sym::sha512_sm_x86)), + ("sm4", Unstable(sym::sha512_sm_x86)), ("sse", Stable), ("sse2", Stable), ("sse3", Stable), @@ -330,12 +333,14 @@ const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ ("mutable-globals", Stable), ("nontrapping-fptoint", Stable), ("reference-types", Unstable(sym::wasm_target_feature)), - ("relaxed-simd", Unstable(sym::wasm_target_feature)), + ("relaxed-simd", Stable), ("sign-ext", Stable), ("simd128", Stable), // tidy-alphabetical-end ]; +const WASM_IMPLICIT_FEATURES: &[(&str, &str)] = &[("relaxed-simd", "simd128")]; + const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ @@ -452,4 +457,13 @@ impl super::spec::Target { _ => &[], } } + + /// Returns a list of target features. Each items first target feature + /// implicitly enables the second one. + pub fn implicit_target_features(&self) -> &'static [(&'static str, &'static str)] { + match &*self.arch { + "wasm32" | "wasm64" => WASM_IMPLICIT_FEATURES, + _ => &[], + } + } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 2ce1b955af521..1cee82f04ea0e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -5,10 +5,11 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{ - pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, StringPart, + pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, + StringPart, }; use rustc_hir::def::Namespace; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; use rustc_infer::infer::{InferOk, TypeTrace}; @@ -1624,9 +1625,131 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { - // If we have a single implementation, try to unify it with the trait ref - // that failed. This should uncover a better hint for what *is* implemented. + let alternative_candidates = |def_id: DefId| { + let mut impl_candidates: Vec<_> = self + .tcx + .all_impls(def_id) + // ignore `do_not_recommend` items + .filter(|def_id| { + !self + .tcx + .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) + }) + // Ignore automatically derived impls and `!Trait` impls. + .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) + .filter_map(|header| { + (header.polarity != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id)) + .then(|| header.trait_ref.instantiate_identity()) + }) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + } else { + true + } + }) + .collect(); + + impl_candidates.sort_by_key(|tr| tr.to_string()); + impl_candidates.dedup(); + impl_candidates + }; + + // We'll check for the case where the reason for the mismatch is that the trait comes from + // one crate version and the type comes from another crate version, even though they both + // are from the same crate. + let trait_def_id = trait_ref.def_id(); + if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() + && let found_type = def.did() + && trait_def_id.krate != found_type.krate + && self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate) + { + let name = self.tcx.crate_name(trait_def_id.krate); + let spans: Vec<_> = [trait_def_id, found_type] + .into_iter() + .filter_map(|def_id| self.tcx.extern_crate(def_id)) + .map(|data| { + let dependency = if data.dependency_of == LOCAL_CRATE { + "direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!("dependency of crate `{dep}`") + }; + ( + data.span, + format!("one version of crate `{name}` is used here, as a {dependency}"), + ) + }) + .collect(); + let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::>().into(); + for (sp, label) in spans.into_iter() { + span.push_span_label(sp, label); + } + err.highlighted_span_help( + span, + vec![ + StringPart::normal("you have ".to_string()), + StringPart::highlighted("multiple different versions".to_string()), + StringPart::normal(" of crate `".to_string()), + StringPart::highlighted(format!("{name}")), + StringPart::normal("` in your dependency graph".to_string()), + ], + ); + let candidates = if impl_candidates.is_empty() { + alternative_candidates(trait_def_id) + } else { + impl_candidates.into_iter().map(|cand| cand.trait_ref).collect() + }; + if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| { + if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind() + && let candidate_def_id = def.did() + && let Some(name) = self.tcx.opt_item_name(candidate_def_id) + && let Some(found) = self.tcx.opt_item_name(found_type) + && name == found + && candidate_def_id.krate != found_type.krate + && self.tcx.crate_name(candidate_def_id.krate) + == self.tcx.crate_name(found_type.krate) + { + // A candidate was found of an item with the same name, from two separate + // versions of the same crate, let's clarify. + Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type))) + } else { + None + } + }) { + let mut span: MultiSpan = vec![sp_candidate, sp_found].into(); + span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait"); + span.push_span_label(sp_candidate, "this type implements the required trait"); + span.push_span_label(sp_found, "this type doesn't implement the required trait"); + err.highlighted_span_note( + span, + vec![ + StringPart::normal( + "two types coming from two different versions of the same crate are \ + different types " + .to_string(), + ), + StringPart::highlighted("even if they look the same".to_string()), + ], + ); + } + err.help("you can use `cargo tree` to explore your dependency tree"); + return true; + } + if let [single] = &impl_candidates { + // If we have a single implementation, try to unify it with the trait ref + // that failed. This should uncover a better hint for what *is* implemented. if self.probe(|_| { let ocx = ObligationCtxt::new(self); @@ -1798,43 +1921,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Mentioning implementers of `Copy`, `Debug` and friends is not useful. return false; } - let mut impl_candidates: Vec<_> = self - .tcx - .all_impls(def_id) - // ignore `do_not_recommend` items - .filter(|def_id| { - !self - .tcx - .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) - }) - // Ignore automatically derived impls and `!Trait` impls. - .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) - .filter_map(|header| { - (header.polarity != ty::ImplPolarity::Negative - || self.tcx.is_automatically_derived(def_id)) - .then(|| header.trait_ref.instantiate_identity()) - }) - .filter(|trait_ref| { - let self_ty = trait_ref.self_ty(); - // Avoid mentioning type parameters. - if let ty::Param(_) = self_ty.kind() { - false - } - // Avoid mentioning types that are private to another crate - else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { - // FIXME(compiler-errors): This could be generalized, both to - // be more granular, and probably look past other `#[fundamental]` - // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) - } else { - true - } - }) - .collect(); - - impl_candidates.sort_by_key(|tr| tr.to_string()); - impl_candidates.dedup(); - return report(impl_candidates, err); + return report(alternative_candidates(def_id), err); } // Sort impl candidates so that ordering is consistent for UI tests. diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 02fc37d87984b..78f1f7d9b9b59 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1582,10 +1582,7 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[multipart_suggestion( - trait_selection_meant_str_literal, - applicability = "machine-applicable" - )] + #[multipart_suggestion(trait_selection_meant_str_literal, applicability = "machine-applicable")] MeantStrLiteral { #[suggestion_part(code = "\"")] start: Span, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index f7b8d99593ec2..a350b76a7049a 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -62,7 +62,7 @@ pub use self::specialize::{ }; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::{ - elaborate, expand_trait_aliases, impl_item_is_final, supertraits, transitive_bounds, + elaborate, expand_trait_aliases, impl_item_is_final, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo, }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 37a16a43acdeb..d3a1ed52d2e62 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -42,8 +42,15 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Foreign(..) | ty::Error(_) => true, - // `T is PAT`, `[T; N]`, and `[T]` have same properties as T. - ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + // `T is PAT` and `[T]` have same properties as T. + ty::Pat(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + ty::Array(ty, size) => { + // Empty array never has a dtor. See issue #110288. + match size.try_to_target_usize(tcx) { + Some(0) => true, + _ => trivial_dropck_outlives(tcx, *ty), + } + } // (T1..Tn) and closures have same properties as T1..Tn -- // check if *all* of them are trivial. diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index cc9174d3aad52..1d9a90f0300ab 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -7,6 +7,7 @@ use std::fmt::{self, Display}; use std::ops::ControlFlow; use std::{cmp, iter}; +use hir::def::DefKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; @@ -14,8 +15,9 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::infer::relate::TypeRelation; -use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; -use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; +use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_infer::traits::util::elaborate; use rustc_infer::traits::TraitObligation; use rustc_middle::bug; use rustc_middle::dep_graph::{dep_kinds, DepNodeIndex}; @@ -2798,6 +2800,35 @@ impl<'tcx> SelectionContext<'_, 'tcx> { }); } + // Register any outlives obligations from the trait here, cc #124336. + if matches!(self.tcx().def_kind(def_id), DefKind::Impl { of_trait: true }) + && let Some(header) = self.tcx().impl_trait_header(def_id) + { + let trait_clause: ty::Clause<'tcx> = + header.trait_ref.instantiate(self.tcx(), args).upcast(self.tcx()); + for clause in elaborate(self.tcx(), [trait_clause]) { + if matches!( + clause.kind().skip_binder(), + ty::ClauseKind::TypeOutlives(..) | ty::ClauseKind::RegionOutlives(..) + ) { + let clause = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + clause, + &mut obligations, + ); + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate: clause.as_predicate(), + }); + } + } + } + obligations } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 673b81f6f400c..3ef10f4e43c4b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -733,9 +733,7 @@ fn coroutine_saved_local_eligibility( // point, so it is no longer a candidate. trace!( "removing local {:?} in >1 variant ({:?}, {:?})", - local, - variant_index, - idx + local, variant_index, idx ); ineligible_locals.insert(*local); assignments[*local] = Ineligible(None); diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 0246996c27f95..f30419c801f18 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -264,15 +264,6 @@ pub fn supertraits( elaborate(cx, [trait_ref.upcast(cx)]).filter_only_self().filter_to_traits() } -pub fn transitive_bounds( - cx: I, - trait_refs: impl Iterator>>, -) -> FilterToTraits> { - elaborate(cx, trait_refs.map(|trait_ref| trait_ref.upcast(cx))) - .filter_only_self() - .filter_to_traits() -} - impl Elaborator { fn filter_to_traits(self) -> FilterToTraits { FilterToTraits { _cx: PhantomData, base_iterator: self } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index e1a3764fa76b4..263ba676427c5 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -451,6 +451,8 @@ pub trait Clause>: + UpcastFrom>> + UpcastFrom> + UpcastFrom>> + + UpcastFrom> + + UpcastFrom>> + UpcastFrom> + UpcastFrom>> + IntoKind>> diff --git a/config.example.toml b/config.example.toml index 45faa66ec114f..1a7bdc4737fbf 100644 --- a/config.example.toml +++ b/config.example.toml @@ -472,7 +472,8 @@ # This is mostly useful for tools; if you have changes to `compiler/` or `library/` they will be ignored. # # Set this to "if-unchanged" to only download if the compiler and standard library have not been modified. -# Set this to `true` to download unconditionally (useful if e.g. you are only changing doc-comments). +# Set this to `true` to download unconditionally. This is useful if you are working on tools, doc-comments, +# or library (you will be able to build the standard library without needing to build the compiler). #download-rustc = false # Number of codegen units to use for each compiler invocation. A value of 0 diff --git a/library/Cargo.lock b/library/Cargo.lock new file mode 100644 index 0000000000000..223b61456c267 --- /dev/null +++ b/library/Cargo.lock @@ -0,0 +1,489 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "compiler_builtins", + "gimli 0.29.0", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "alloc" +version = "0.0.0" +dependencies = [ + "compiler_builtins", + "core", + "rand", + "rand_xorshift", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "cc" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "compiler_builtins" +version = "0.1.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb58b199190fcfe0846f55a3b545cd6b07a34bdd5930a476ff856f3ebcc5558a" +dependencies = [ + "cc", + "rustc-std-workspace-core", +] + +[[package]] +name = "core" +version = "0.0.0" +dependencies = [ + "rand", + "rand_xorshift", +] + +[[package]] +name = "dlmalloc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d" +dependencies = [ + "cfg-if", + "compiler_builtins", + "libc", + "rustc-std-workspace-core", + "windows-sys", +] + +[[package]] +name = "fortanix-sgx-abi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "rustc-std-workspace-core", + "rustc-std-workspace-std", + "unicode-width", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "allocator-api2", + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "object" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +dependencies = [ + "compiler_builtins", + "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "panic_abort" +version = "0.0.0" +dependencies = [ + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "libc", +] + +[[package]] +name = "panic_unwind" +version = "0.0.0" +dependencies = [ + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "libc", + "unwind", +] + +[[package]] +name = "proc_macro" +version = "0.0.0" +dependencies = [ + "core", + "std", +] + +[[package]] +name = "profiler_builtins" +version = "0.0.0" +dependencies = [ + "cc", + "compiler_builtins", + "core", +] + +[[package]] +name = "r-efi" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "r-efi-alloc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" +dependencies = [ + "compiler_builtins", + "r-efi", + "rustc-std-workspace-core", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.99.0" +dependencies = [ + "alloc", +] + +[[package]] +name = "rustc-std-workspace-core" +version = "1.99.0" +dependencies = [ + "core", +] + +[[package]] +name = "rustc-std-workspace-std" +version = "1.99.0" +dependencies = [ + "std", +] + +[[package]] +name = "std" +version = "0.0.0" +dependencies = [ + "addr2line", + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "dlmalloc", + "fortanix-sgx-abi", + "hashbrown", + "hermit-abi", + "libc", + "miniz_oxide", + "object", + "panic_abort", + "panic_unwind", + "profiler_builtins", + "r-efi", + "r-efi-alloc", + "rand", + "rand_xorshift", + "rustc-demangle", + "std_detect", + "unwind", + "wasi", +] + +[[package]] +name = "std_detect" +version = "0.1.5" +dependencies = [ + "cfg-if", + "compiler_builtins", + "libc", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "sysroot" +version = "0.0.0" +dependencies = [ + "proc_macro", + "std", + "test", +] + +[[package]] +name = "test" +version = "0.0.0" +dependencies = [ + "core", + "getopts", + "libc", + "std", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", + "rustc-std-workspace-std", +] + +[[package]] +name = "unwind" +version = "0.0.0" +dependencies = [ + "cfg-if", + "compiler_builtins", + "core", + "libc", + "unwinding", +] + +[[package]] +name = "unwinding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" +dependencies = [ + "compiler_builtins", + "gimli 0.28.1", + "rustc-std-workspace-core", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/library/Cargo.toml b/library/Cargo.toml new file mode 100644 index 0000000000000..c4513b4c127d8 --- /dev/null +++ b/library/Cargo.toml @@ -0,0 +1,44 @@ +[workspace] +resolver = "1" +members = [ + "std", + "sysroot", +] + +exclude = [ + # stdarch has its own Cargo workspace + "stdarch", +] + +[profile.release.package.compiler_builtins] +# For compiler-builtins we always use a high number of codegen units. +# The goal here is to place every single intrinsic into its own object +# file to avoid symbol clashes with the system libgcc if possible. Note +# that this number doesn't actually produce this many object files, we +# just don't create more than this number of object files. +# +# It's a bit of a bummer that we have to pass this here, unfortunately. +# Ideally this would be specified through an env var to Cargo so Cargo +# knows how many CGUs are for this specific crate, but for now +# per-crate configuration isn't specifiable in the environment. +codegen-units = 10000 + +# These dependencies of the standard library implement symbolication for +# backtraces on most platforms. Their debuginfo causes both linking to be slower +# (more data to chew through) and binaries to be larger without really all that +# much benefit. This section turns them all to down to have no debuginfo which +# helps to improve link times a little bit. +[profile.release.package] +addr2line.debug = 0 +adler.debug = 0 +gimli.debug = 0 +miniz_oxide.debug = 0 +object.debug = 0 +rustc-demangle.debug = 0 + +[patch.crates-io] +# See comments in `library/rustc-std-workspace-core/README.md` for what's going on +# here +rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } +rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } +rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ef40d3b203f48..7de412595993a 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -200,7 +200,7 @@ use core::ops::{ AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver, }; -use core::pin::Pin; +use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, addr_of_mut, NonNull, Unique}; use core::task::{Context, Poll}; use core::{borrow, fmt, slice}; @@ -1173,6 +1173,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Self) -> *mut T { @@ -1226,6 +1227,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { @@ -2724,3 +2726,6 @@ impl core::error::Error for Box { core::error::Error::provide(&**self, request); } } + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Box {} diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 74b000fd1c40b..973e7c660670c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, Peekable}; use core::mem::ManuallyDrop; -use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; +use core::ops::{BitAnd, BitOr, BitXor, Bound, RangeBounds, Sub}; use super::map::{BTreeMap, Keys}; use super::merge_iter::MergeIterInner; @@ -1182,6 +1182,178 @@ impl BTreeSet { pub const fn is_empty(&self) -> bool { self.len() == 0 } + + /// Returns a [`Cursor`] pointing at the gap before the smallest element + /// greater than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap before the smallest element greater than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap before the smallest element greater than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap before the smallest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let cursor = set.lower_bound(Bound::Included(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&1)); + /// assert_eq!(cursor.peek_next(), Some(&2)); + /// + /// let cursor = set.lower_bound(Bound::Excluded(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let cursor = set.lower_bound(Bound::Unbounded); + /// assert_eq!(cursor.peek_prev(), None); + /// assert_eq!(cursor.peek_next(), Some(&1)); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn lower_bound(&self, bound: Bound<&Q>) -> Cursor<'_, T> + where + T: Borrow + Ord, + Q: Ord, + { + Cursor { inner: self.map.lower_bound(bound) } + } + + /// Returns a [`CursorMut`] pointing at the gap before the smallest element + /// greater than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap before the smallest element greater than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap before the smallest element greater than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap before the smallest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let mut set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let mut cursor = set.lower_bound_mut(Bound::Included(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&1)); + /// assert_eq!(cursor.peek_next(), Some(&2)); + /// + /// let mut cursor = set.lower_bound_mut(Bound::Excluded(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let mut cursor = set.lower_bound_mut(Bound::Unbounded); + /// assert_eq!(cursor.peek_prev(), None); + /// assert_eq!(cursor.peek_next(), Some(&1)); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn lower_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + where + T: Borrow + Ord, + Q: Ord, + { + CursorMut { inner: self.map.lower_bound_mut(bound) } + } + + /// Returns a [`Cursor`] pointing at the gap after the greatest element + /// smaller than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap after the greatest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let cursor = set.upper_bound(Bound::Included(&3)); + /// assert_eq!(cursor.peek_prev(), Some(&3)); + /// assert_eq!(cursor.peek_next(), Some(&4)); + /// + /// let cursor = set.upper_bound(Bound::Excluded(&3)); + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let cursor = set.upper_bound(Bound::Unbounded); + /// assert_eq!(cursor.peek_prev(), Some(&4)); + /// assert_eq!(cursor.peek_next(), None); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn upper_bound(&self, bound: Bound<&Q>) -> Cursor<'_, T> + where + T: Borrow + Ord, + Q: Ord, + { + Cursor { inner: self.map.upper_bound(bound) } + } + + /// Returns a [`CursorMut`] pointing at the gap after the greatest element + /// smaller than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap after the greatest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let mut set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Included(&3)) }; + /// assert_eq!(cursor.peek_prev(), Some(&3)); + /// assert_eq!(cursor.peek_next(), Some(&4)); + /// + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Excluded(&3)) }; + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Unbounded) }; + /// assert_eq!(cursor.peek_prev(), Some(&4)); + /// assert_eq!(cursor.peek_next(), None); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + where + T: Borrow + Ord, + Q: Ord, + { + CursorMut { inner: self.map.upper_bound_mut(bound) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1816,5 +1988,414 @@ impl<'a, T: Ord> Iterator for Union<'a, T> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Union<'_, T> {} +/// A cursor over a `BTreeSet`. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. +/// +/// Cursors always point to a gap between two elements in the set, and can +/// operate on the two immediately adjacent elements. +/// +/// A `Cursor` is created with the [`BTreeSet::lower_bound`] and [`BTreeSet::upper_bound`] methods. +#[derive(Clone)] +#[unstable(feature = "btree_cursors", issue = "107540")] +pub struct Cursor<'a, K: 'a> { + inner: super::map::Cursor<'a, K, SetValZST>, +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +impl Debug for Cursor<'_, K> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Cursor") + } +} + +/// A cursor over a `BTreeSet` with editing operations. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the set during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying map. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always point to a gap between two elements in the set, and can +/// operate on the two immediately adjacent elements. +/// +/// A `CursorMut` is created with the [`BTreeSet::lower_bound_mut`] and [`BTreeSet::upper_bound_mut`] +/// methods. +#[unstable(feature = "btree_cursors", issue = "107540")] +pub struct CursorMut<'a, K: 'a, #[unstable(feature = "allocator_api", issue = "32838")] A = Global> +{ + inner: super::map::CursorMut<'a, K, SetValZST, A>, +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +impl Debug for CursorMut<'_, K, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CursorMut") + } +} + +/// A cursor over a `BTreeSet` with editing operations, and which allows +/// mutating elements. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the set during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying set. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always point to a gap between two elements in the set, and can +/// operate on the two immediately adjacent elements. +/// +/// A `CursorMutKey` is created from a [`CursorMut`] with the +/// [`CursorMut::with_mutable_key`] method. +/// +/// # Safety +/// +/// Since this cursor allows mutating elements, you must ensure that the +/// `BTreeSet` invariants are maintained. Specifically: +/// +/// * The newly inserted element must be unique in the tree. +/// * All elements in the tree must remain in sorted order. +#[unstable(feature = "btree_cursors", issue = "107540")] +pub struct CursorMutKey< + 'a, + K: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A = Global, +> { + inner: super::map::CursorMutKey<'a, K, SetValZST, A>, +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +impl Debug for CursorMutKey<'_, K, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CursorMutKey") + } +} + +impl<'a, K> Cursor<'a, K> { + /// Advances the cursor to the next gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the end of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn next(&mut self) -> Option<&'a K> { + self.inner.next().map(|(k, _)| k) + } + + /// Advances the cursor to the previous gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the start of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn prev(&mut self) -> Option<&'a K> { + self.inner.prev().map(|(k, _)| k) + } + + /// Returns a reference to next element without moving the cursor. + /// + /// If the cursor is at the end of the set then `None` is returned + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_next(&self) -> Option<&'a K> { + self.inner.peek_next().map(|(k, _)| k) + } + + /// Returns a reference to the previous element without moving the cursor. + /// + /// If the cursor is at the start of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_prev(&self) -> Option<&'a K> { + self.inner.peek_prev().map(|(k, _)| k) + } +} + +impl<'a, T, A> CursorMut<'a, T, A> { + /// Advances the cursor to the next gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the end of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn next(&mut self) -> Option<&T> { + self.inner.next().map(|(k, _)| k) + } + + /// Advances the cursor to the previous gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the start of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn prev(&mut self) -> Option<&T> { + self.inner.prev().map(|(k, _)| k) + } + + /// Returns a reference to the next element without moving the cursor. + /// + /// If the cursor is at the end of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_next(&mut self) -> Option<&T> { + self.inner.peek_next().map(|(k, _)| k) + } + + /// Returns a reference to the previous element without moving the cursor. + /// + /// If the cursor is at the start of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_prev(&mut self) -> Option<&T> { + self.inner.peek_prev().map(|(k, _)| k) + } + + /// Returns a read-only cursor pointing to the same location as the + /// `CursorMut`. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the `Cursor`. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn as_cursor(&self) -> Cursor<'_, T> { + Cursor { inner: self.inner.as_cursor() } + } + + /// Converts the cursor into a [`CursorMutKey`], which allows mutating + /// elements in the tree. + /// + /// # Safety + /// + /// Since this cursor allows mutating elements, you must ensure that the + /// `BTreeSet` invariants are maintained. Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn with_mutable_key(self) -> CursorMutKey<'a, T, A> { + CursorMutKey { inner: unsafe { self.inner.with_mutable_key() } } + } +} + +impl<'a, T, A> CursorMutKey<'a, T, A> { + /// Advances the cursor to the next gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the end of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn next(&mut self) -> Option<&mut T> { + self.inner.next().map(|(k, _)| k) + } + + /// Advances the cursor to the previous gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the start of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn prev(&mut self) -> Option<&mut T> { + self.inner.prev().map(|(k, _)| k) + } + + /// Returns a reference to the next element without moving the cursor. + /// + /// If the cursor is at the end of the set then `None` is returned + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_next(&mut self) -> Option<&mut T> { + self.inner.peek_next().map(|(k, _)| k) + } + + /// Returns a reference to the previous element without moving the cursor. + /// + /// If the cursor is at the start of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_prev(&mut self) -> Option<&mut T> { + self.inner.peek_prev().map(|(k, _)| k) + } + + /// Returns a read-only cursor pointing to the same location as the + /// `CursorMutKey`. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMutKey`, which means it cannot outlive the `CursorMutKey` and that the + /// `CursorMutKey` is frozen for the lifetime of the `Cursor`. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn as_cursor(&self) -> Cursor<'_, T> { + Cursor { inner: self.inner.as_cursor() } + } +} + +impl<'a, T: Ord, A: Allocator + Clone> CursorMut<'a, T, A> { + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_after_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_after_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_before_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_before_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_after(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_after(value, SetValZST) + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_before(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_before(value, SetValZST) + } + + /// Removes the next element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (before the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_next(&mut self) -> Option { + self.inner.remove_next().map(|(k, _)| k) + } + + /// Removes the precending element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (after the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_prev(&mut self) -> Option { + self.inner.remove_prev().map(|(k, _)| k) + } +} + +impl<'a, T: Ord, A: Allocator + Clone> CursorMutKey<'a, T, A> { + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The key of the newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_after_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_after_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_before_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_before_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_after(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_after(value, SetValZST) + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_before(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_before(value, SetValZST) + } + + /// Removes the next element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (before the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_next(&mut self) -> Option { + self.inner.remove_next().map(|(k, _)| k) + } + + /// Removes the precending element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (after the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_prev(&mut self) -> Option { + self.inner.remove_prev().map(|(k, _)| k) + } +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +pub use super::map::UnorderedKeyError; + #[cfg(test)] mod tests; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 6c58594605004..3e44adf73f045 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -138,6 +138,7 @@ #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] +#![feature(pin_coerce_unsized_trait)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] @@ -165,7 +166,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(c_unwind))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index bc0874fc13f46..bdee06154faec 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -256,6 +256,7 @@ use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Rece use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] use core::pin::Pin; +use core::pin::PinCoerceUnsized; use core::ptr::{self, drop_in_place, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; @@ -1372,6 +1373,7 @@ impl Rc { /// let x = unsafe { Rc::from_raw_in(ptr, alloc) }; /// assert_eq!(&*x, "hello"); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = mem::ManuallyDrop::new(this); @@ -2176,6 +2178,12 @@ impl Deref for Rc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Rc {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Weak {} + #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Rc {} @@ -3107,6 +3115,7 @@ impl Weak { /// /// [`from_raw_in`]: Weak::from_raw_in /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn into_raw_with_allocator(self) -> (*const T, A) { @@ -3689,6 +3698,9 @@ impl Deref for UniqueRc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UniqueRc {} + #[unstable(feature = "unique_rc_arc", issue = "112566")] impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 01fe950cf3fdd..124230812df56 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -900,7 +900,7 @@ impl String { /// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) }; /// assert_eq!(rebuilt, "hello"); /// ``` - #[must_use = "`self` will be dropped if the result is not used"] + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { self.vec.into_raw_parts() diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 3ad0dae77dbde..2c0d19b0ada09 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -20,7 +20,7 @@ use core::marker::{PhantomData, Unsize}; use core::mem::{self, align_of_val_raw, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::pin::Pin; +use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; @@ -2142,6 +2142,12 @@ impl Deref for Arc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Arc {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Weak {} + #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Arc {} diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index dfd22204c81fa..b4e0bc5fcbe41 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -878,6 +878,7 @@ impl Vec { /// }; /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); @@ -921,6 +922,7 @@ impl Vec { /// }; /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { diff --git a/library/alloc/tests/arc.rs b/library/alloc/tests/arc.rs index c37a80dca95c8..dc27c578b57ef 100644 --- a/library/alloc/tests/arc.rs +++ b/library/alloc/tests/arc.rs @@ -227,3 +227,17 @@ fn make_mut_unsized() { assert_eq!(*data, [11, 21, 31]); assert_eq!(*other_data, [110, 20, 30]); } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::sync::Arc; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Arc + pub fn pin_arc(arg: Pin>) -> Pin> { + arg + } +} diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index 4cacee0414d7d..faee64b2f6738 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -179,3 +179,40 @@ unsafe impl Allocator for ConstAllocator { self } } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::boxed::Box; + use core::pin::Pin; + + trait MyTrait { + fn action(&self) -> &str; + } + impl MyTrait for String { + fn action(&self) -> &str { + &*self + } + } + struct MyStruct; + impl MyTrait for MyStruct { + fn action(&self) -> &str { + "MyStruct" + } + } + + // Pin coercion should work for Box + fn pin_box(arg: Pin>) -> Pin> { + arg + } + + #[test] + fn pin_coerce_unsized_box() { + let my_string = "my string"; + let a_string = Box::pin(String::from(my_string)); + let pin_box_str = pin_box(a_string); + assert_eq!(pin_box_str.as_ref().action(), my_string); + let a_struct = Box::pin(MyStruct); + let pin_box_struct = pin_box(a_struct); + assert_eq!(pin_box_struct.as_ref().action(), "MyStruct"); + } +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 89538f272f069..3d4add6fae452 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -40,6 +40,7 @@ #![feature(drain_keep_rest)] #![feature(local_waker)] #![feature(vec_pop_if)] +#![feature(unique_rc_arc)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloc/tests/rc.rs b/library/alloc/tests/rc.rs index 499740e738ab0..29dbdcf225eb5 100644 --- a/library/alloc/tests/rc.rs +++ b/library/alloc/tests/rc.rs @@ -205,3 +205,20 @@ fn weak_may_dangle() { // `val` dropped here while still borrowed // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak` } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::rc::{Rc, UniqueRc}; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Rc + pub fn pin_rc(arg: Pin>) -> Pin> { + arg + } + pub fn pin_unique_rc(arg: Pin>) -> Pin> { + arg + } +} diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 31d6bc36fc8b9..d681bd124fe13 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -4,6 +4,15 @@ #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; +#[cfg(bootstrap)] +#[allow(dead_code)] +#[unstable(feature = "sha512_sm_x86", issue = "126624")] +fn dummy() { + // AArch64 also has a target feature named `sm4`, so we need `#![feature(sha512_sm_x86)]` in lib.rs + // But as the bootstrap compiler doesn't know about this feature yet, we need to convert it to a + // library feature until bootstrap gets bumped +} + /// Inline assembly. /// /// Refer to [Rust By Example] for a usage guide and the [reference] for diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 0d66c2b52c84e..d860f3415d982 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -255,6 +255,7 @@ use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; mod lazy; @@ -2396,3 +2397,21 @@ fn assert_coerce_unsized( let _: Cell<&dyn Send> = c; let _: RefCell<&dyn Send> = d; } + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UnsafeCell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for SyncUnsafeCell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Cell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for RefCell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<'b, T: ?Sized> PinCoerceUnsized for Ref<'b, T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<'b, T: ?Sized> PinCoerceUnsized for RefMut<'b, T> {} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index cece00c9c0807..41a19665779b6 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -222,10 +222,7 @@ impl char { /// assert_eq!('❤', c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_stable( - feature = "const_char_from_u32_unchecked", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 625ac5ae2b45e..e6574ac3c7278 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -125,7 +125,7 @@ pub const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] -#[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 4524b352ec817..5cacedcb241a5 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar; /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Default")] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)] pub trait Default: Sized { /// Returns the "default value" for a type. /// diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 4d3b22d4eed8f..6cc91849e1dc9 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -1,5 +1,5 @@ #![doc = include_str!("error.md")] -#![stable(feature = "error_in_core", since = "CURRENT_RUSTC_VERSION")] +#![stable(feature = "error_in_core", since = "1.81.0")] #[cfg(test)] mod tests; diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index b1f731c5fe097..22084dcff8f88 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -272,7 +272,7 @@ impl CStr { #[inline] // inline is necessary for codegen to see strlen. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { // SAFETY: The caller has provided a pointer that points to a valid C // string with a NUL terminator less than `isize::MAX` from `ptr`. @@ -534,7 +534,7 @@ impl CStr { #[must_use] #[doc(alias("len", "strlen"))] #[stable(feature = "cstr_count_bytes", since = "1.79.0")] - #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] pub const fn count_bytes(&self) -> usize { self.inner.len() - 1 } @@ -729,7 +729,7 @@ impl AsRef for CStr { /// located within `isize::MAX` from `ptr`. #[inline] #[unstable(feature = "cstr_internals", issue = "none")] -#[rustc_const_stable(feature = "const_cstr_from_ptr", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] #[rustc_allow_const_fn_unstable(const_eval_select)] const unsafe fn strlen(ptr: *const c_char) -> usize { const fn strlen_ct(s: *const c_char) -> usize { diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index bb969cf71c57e..6ca5e53df3b01 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -194,8 +194,8 @@ pub const unsafe fn unreachable_unchecked() -> ! { #[track_caller] #[inline(always)] #[doc(alias = "assume")] -#[stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "hint_assert_unchecked", since = "1.81.0")] +#[rustc_const_stable(feature = "hint_assert_unchecked", since = "1.81.0")] pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 874a5b888af5f..6f1e0cb747194 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1043,45 +1043,6 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn breakpoint(); - #[cfg(bootstrap)] - #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn size_of() -> usize; - - #[cfg(bootstrap)] - #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn min_align_of() -> usize; - - #[cfg(bootstrap)] - #[rustc_const_unstable(feature = "const_pref_align_of", issue = "91971")] - #[rustc_nounwind] - pub fn pref_align_of() -> usize; - - #[cfg(bootstrap)] - #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] - #[rustc_nounwind] - pub fn size_of_val(_: *const T) -> usize; - - #[cfg(bootstrap)] - #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] - #[rustc_nounwind] - pub fn min_align_of_val(_: *const T) -> usize; - - #[cfg(bootstrap)] - #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn type_name() -> &'static str; - - #[cfg(bootstrap)] - #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn type_id() -> u128; - /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. /// @@ -1567,6 +1528,12 @@ extern "rust-intrinsic" { #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] pub fn unaligned_volatile_store(dst: *mut T, val: T); + /// Returns the square root of an `f16` + /// + /// The stabilized version of this intrinsic is + /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is @@ -1579,6 +1546,12 @@ extern "rust-intrinsic" { /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_nounwind] pub fn sqrtf64(x: f64) -> f64; + /// Returns the square root of an `f128` + /// + /// The stabilized version of this intrinsic is + /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1605,6 +1578,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn powif128(a: f128, x: i32) -> f128; + /// Returns the sine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::sin`](../../std/primitive.f16.html#method.sin) + #[rustc_nounwind] + pub fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1617,7 +1596,19 @@ extern "rust-intrinsic" { /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_nounwind] pub fn sinf64(x: f64) -> f64; + /// Returns the sine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::sin`](../../std/primitive.f128.html#method.sin) + #[rustc_nounwind] + pub fn sinf128(x: f128) -> f128; + /// Returns the cosine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::cos`](../../std/primitive.f16.html#method.cos) + #[rustc_nounwind] + pub fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1630,7 +1621,19 @@ extern "rust-intrinsic" { /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_nounwind] pub fn cosf64(x: f64) -> f64; + /// Returns the cosine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::cos`](../../std/primitive.f128.html#method.cos) + #[rustc_nounwind] + pub fn cosf128(x: f128) -> f128; + /// Raises an `f16` to an `f16` power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powf`](../../std/primitive.f16.html#method.powf) + #[rustc_nounwind] + pub fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is @@ -1643,7 +1646,19 @@ extern "rust-intrinsic" { /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_nounwind] pub fn powf64(a: f64, x: f64) -> f64; + /// Raises an `f128` to an `f128` power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powf`](../../std/primitive.f128.html#method.powf) + #[rustc_nounwind] + pub fn powf128(a: f128, x: f128) -> f128; + /// Returns the exponential of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp`](../../std/primitive.f16.html#method.exp) + #[rustc_nounwind] + pub fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1656,7 +1671,19 @@ extern "rust-intrinsic" { /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_nounwind] pub fn expf64(x: f64) -> f64; + /// Returns the exponential of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp`](../../std/primitive.f128.html#method.exp) + #[rustc_nounwind] + pub fn expf128(x: f128) -> f128; + /// Returns 2 raised to the power of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1669,7 +1696,19 @@ extern "rust-intrinsic" { /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_nounwind] pub fn exp2f64(x: f64) -> f64; + /// Returns 2 raised to the power of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f128(x: f128) -> f128; + /// Returns the natural logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ln`](../../std/primitive.f16.html#method.ln) + #[rustc_nounwind] + pub fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1682,7 +1721,19 @@ extern "rust-intrinsic" { /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_nounwind] pub fn logf64(x: f64) -> f64; + /// Returns the natural logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ln`](../../std/primitive.f128.html#method.ln) + #[rustc_nounwind] + pub fn logf128(x: f128) -> f128; + /// Returns the base 10 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log10`](../../std/primitive.f16.html#method.log10) + #[rustc_nounwind] + pub fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1695,7 +1746,19 @@ extern "rust-intrinsic" { /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_nounwind] pub fn log10f64(x: f64) -> f64; + /// Returns the base 10 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log10`](../../std/primitive.f128.html#method.log10) + #[rustc_nounwind] + pub fn log10f128(x: f128) -> f128; + /// Returns the base 2 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log2`](../../std/primitive.f16.html#method.log2) + #[rustc_nounwind] + pub fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1708,7 +1771,19 @@ extern "rust-intrinsic" { /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_nounwind] pub fn log2f64(x: f64) -> f64; + /// Returns the base 2 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log2`](../../std/primitive.f128.html#method.log2) + #[rustc_nounwind] + pub fn log2f128(x: f128) -> f128; + /// Returns `a * b + c` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1721,7 +1796,19 @@ extern "rust-intrinsic" { /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_nounwind] pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + /// Returns the absolute value of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::abs`](../../std/primitive.f16.html#method.abs) + #[rustc_nounwind] + pub fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1734,7 +1821,25 @@ extern "rust-intrinsic" { /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] pub fn fabsf64(x: f64) -> f64; + /// Returns the absolute value of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::abs`](../../std/primitive.f128.html#method.abs) + #[rustc_nounwind] + pub fn fabsf128(x: f128) -> f128; + /// Returns the minimum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf16(x: f16, y: f16) -> f16; /// Returns the minimum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1759,6 +1864,31 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn minnumf64(x: f64, y: f64) -> f64; + /// Returns the minimum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf128(x: f128, y: f128) -> f128; + + /// Returns the maximum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf16(x: f16, y: f16) -> f16; /// Returns the maximum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1783,7 +1913,25 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn maxnumf64(x: f64, y: f64) -> f64; + /// Returns the maximum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf128(x: f128, y: f128) -> f128; + /// Copies the sign from `y` to `x` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1796,7 +1944,19 @@ extern "rust-intrinsic" { /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] pub fn copysignf64(x: f64, y: f64) -> f64; + /// Copies the sign from `y` to `x` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf128(x: f128, y: f128) -> f128; + /// Returns the largest integer less than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::floor`](../../std/primitive.f16.html#method.floor) + #[rustc_nounwind] + pub fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1809,7 +1969,19 @@ extern "rust-intrinsic" { /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_nounwind] pub fn floorf64(x: f64) -> f64; + /// Returns the largest integer less than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::floor`](../../std/primitive.f128.html#method.floor) + #[rustc_nounwind] + pub fn floorf128(x: f128) -> f128; + /// Returns the smallest integer greater than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1822,7 +1994,19 @@ extern "rust-intrinsic" { /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_nounwind] pub fn ceilf64(x: f64) -> f64; + /// Returns the smallest integer greater than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf128(x: f128) -> f128; + /// Returns the integer part of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) + #[rustc_nounwind] + pub fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1835,7 +2019,25 @@ extern "rust-intrinsic" { /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_nounwind] pub fn truncf64(x: f64) -> f64; + /// Returns the integer part of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) + #[rustc_nounwind] + pub fn truncf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1860,7 +2062,25 @@ extern "rust-intrinsic" { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1873,7 +2093,19 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn nearbyintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round`](../../std/primitive.f16.html#method.round) + #[rustc_nounwind] + pub fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1886,7 +2118,19 @@ extern "rust-intrinsic" { /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_nounwind] pub fn roundf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round`](../../std/primitive.f128.html#method.round) + #[rustc_nounwind] + pub fn roundf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number /// with an even least significant digit. /// @@ -1899,6 +2143,12 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn roundevenf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -2411,12 +2661,6 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn discriminant_value(v: &T) -> ::Discriminant; - #[cfg(bootstrap)] - #[rustc_const_unstable(feature = "variant_count", issue = "73662")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn variant_count() -> usize; - /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. /// @@ -2481,11 +2725,13 @@ extern "rust-intrinsic" { /// /// # Safety /// - /// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized or carry a - /// pointer value. + /// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized. /// Note that this is a stricter criterion than just the *values* being /// fully-initialized: if `T` has padding, it's UB to call this intrinsic. /// + /// At compile-time, it is furthermore UB to call this if any of the bytes + /// in `*a` or `*b` have provenance. + /// /// (The implementation is allowed to branch on the results of comparisons, /// which is UB if any of their inputs are `undef`.) #[rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none")] @@ -2794,7 +3040,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const fn size_of() -> usize { unreachable!() } @@ -2812,7 +3057,6 @@ pub const fn size_of() -> usize { #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const fn min_align_of() -> usize { unreachable!() } @@ -2826,7 +3070,6 @@ pub const fn min_align_of() -> usize { #[rustc_const_unstable(feature = "const_pref_align_of", issue = "91971")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const unsafe fn pref_align_of() -> usize { unreachable!() } @@ -2845,7 +3088,6 @@ pub const unsafe fn pref_align_of() -> usize { #[rustc_const_unstable(feature = "variant_count", issue = "73662")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const fn variant_count() -> usize { unreachable!() } @@ -2862,7 +3104,6 @@ pub const fn variant_count() -> usize { #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const unsafe fn size_of_val(_ptr: *const T) -> usize { unreachable!() } @@ -2879,7 +3120,6 @@ pub const unsafe fn size_of_val(_ptr: *const T) -> usize { #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { unreachable!() } @@ -2897,7 +3137,6 @@ pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const fn type_name() -> &'static str { unreachable!() } @@ -2917,7 +3156,6 @@ pub const fn type_name() -> &'static str { #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[cfg(not(bootstrap))] pub const fn type_id() -> u128 { unreachable!() } diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index fd49a96eaa049..c7cec396e1f2e 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -247,6 +247,8 @@ //! otherwise branch. //! - [`Call`] has an associated function as well, with special syntax: //! `Call(ret_val = function(arg1, arg2, ...), ReturnTo(next_block), UnwindContinue())`. +//! - [`TailCall`] does not have a return destination or next block, so its syntax is just +//! `TailCall(function(arg1, arg2, ...))`. #![unstable( feature = "custom_mir", @@ -350,6 +352,12 @@ define!("mir_call", /// - [`UnwindCleanup`] fn Call(call: (), goto: ReturnToArg, unwind_action: UnwindActionArg) ); +define!("mir_tail_call", + /// Call a function. + /// + /// The argument must be of the form `fun(arg1, arg2, ...)`. + fn TailCall(call: T) +); define!("mir_unwind_resume", /// A terminator that resumes the unwinding. fn UnwindResume() diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 8390dab8e543e..4c4ae39f836ca 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -114,19 +114,12 @@ impl Iterator for RepeatN { #[inline] fn next(&mut self) -> Option { - if self.count == 0 { - return None; - } - - self.count -= 1; - Some(if self.count == 0 { - // SAFETY: the check above ensured that the count used to be non-zero, - // so element hasn't been dropped yet, and we just lowered the count to - // zero so it won't be dropped later, and thus it's okay to take it here. - unsafe { ManuallyDrop::take(&mut self.element) } + if self.count > 0 { + // SAFETY: Just checked it's not empty + unsafe { Some(self.next_unchecked()) } } else { - A::clone(&self.element) - }) + None + } } #[inline] @@ -194,4 +187,18 @@ impl FusedIterator for RepeatN {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RepeatN {} #[unstable(feature = "trusted_len_next_unchecked", issue = "37572")] -impl UncheckedIterator for RepeatN {} +impl UncheckedIterator for RepeatN { + #[inline] + unsafe fn next_unchecked(&mut self) -> Self::Item { + // SAFETY: The caller promised the iterator isn't empty + self.count = unsafe { self.count.unchecked_sub(1) }; + if self.count == 0 { + // SAFETY: the check above ensured that the count used to be non-zero, + // so element hasn't been dropped yet, and we just lowered the count to + // zero so it won't be dropped later, and thus it's okay to take it here. + unsafe { ManuallyDrop::take(&mut self.element) } + } else { + A::clone(&self.element) + } + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d9c7a0877399b..e74900ff7471b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -192,8 +192,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(c_unwind))] -#![cfg_attr(bootstrap, feature(effects))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -262,9 +260,11 @@ #![feature(powerpc_target_feature)] #![feature(riscv_target_feature)] #![feature(rtm_target_feature)] +#![feature(sha512_sm_x86)] #![feature(sse4a_target_feature)] #![feature(tbm_target_feature)] #![feature(wasm_target_feature)] +#![feature(x86_amx_intrinsics)] // tidy-alphabetical-end // allow using `core::` in intra-doc links diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 082768ea9f26d..6a83ec2eb1e0e 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1060,7 +1060,6 @@ pub trait FnPtr: Copy + Clone { } /// Derive macro generating impls of traits related to smart pointers. -#[cfg(not(bootstrap))] #[rustc_builtin_macro] #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] #[unstable(feature = "derive_smart_pointer", issue = "123430")] @@ -1078,7 +1077,6 @@ pub macro SmartPointer($item:item) { reason = "internal module for implementing effects" )] #[allow(missing_debug_implementations)] // these unit structs don't need `Debug` impls. -#[cfg(not(bootstrap))] pub mod effects { #[lang = "EffectsNoRuntime"] pub struct NoRuntime; diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 0192369c0f9eb..0c04f47fe7df1 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -234,24 +234,20 @@ impl f128 { /// This constant isn't guaranteed to equal to any specific NaN bitpattern, /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. - #[cfg(not(bootstrap))] #[allow(clippy::eq_op)] #[rustc_diagnostic_item = "f128_nan"] #[unstable(feature = "f128", issue = "116909")] pub const NAN: f128 = 0.0_f128 / 0.0_f128; /// Infinity (∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] pub const INFINITY: f128 = 1.0_f128 / 0.0_f128; /// Negative infinity (−∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; /// Sign bit - #[cfg(not(bootstrap))] pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; /// Exponent mask @@ -261,11 +257,9 @@ impl f128 { pub(crate) const MAN_MASK: u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff; /// Minimum representable positive value (min subnormal) - #[cfg(not(bootstrap))] const TINY_BITS: u128 = 0x1; /// Minimum representable negative value (min negative subnormal) - #[cfg(not(bootstrap))] const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK; /// Returns `true` if this value is NaN. @@ -284,7 +278,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { @@ -295,7 +288,6 @@ impl f128 { // concerns about portability, so this implementation is for // private use internally. #[inline] - #[cfg(not(bootstrap))] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub(crate) const fn abs_private(self) -> f128 { // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. @@ -326,7 +318,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_infinite(self) -> bool { @@ -354,7 +345,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_finite(self) -> bool { @@ -389,7 +379,6 @@ impl f128 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_subnormal(self) -> bool { @@ -422,7 +411,6 @@ impl f128 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_normal(self) -> bool { @@ -448,7 +436,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn classify(self) -> FpCategory { @@ -557,7 +544,6 @@ impl f128 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_up(self) -> Self { @@ -612,7 +598,6 @@ impl f128 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_down(self) -> Self { @@ -649,7 +634,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn recip(self) -> Self { @@ -670,7 +654,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_degrees(self) -> Self { @@ -694,7 +677,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_radians(self) -> f128 { @@ -704,6 +686,182 @@ impl f128 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f128) -> f128 { + intrinsics::maxnumf128(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f128) -> f128 { + intrinsics::minnumf128(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f128) -> f128 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f128) -> f128 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(num_midpoint)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// assert_eq!(1f128.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f128).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f128) -> f128 { + const LO: f128 = f128::MIN_POSITIVE * 2.; + const HI: f128 = f128::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve `a` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// @@ -1141,7 +1299,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i128; @@ -1201,7 +1358,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn clamp(mut self, min: f128, max: f128) -> f128 { diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e96ffbd8e8ad6..e5b1148e19215 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -229,24 +229,20 @@ impl f16 { /// This constant isn't guaranteed to equal to any specific NaN bitpattern, /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. - #[cfg(not(bootstrap))] #[allow(clippy::eq_op)] #[rustc_diagnostic_item = "f16_nan"] #[unstable(feature = "f16", issue = "116909")] pub const NAN: f16 = 0.0_f16 / 0.0_f16; /// Infinity (∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] pub const INFINITY: f16 = 1.0_f16 / 0.0_f16; /// Negative infinity (−∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; /// Sign bit - #[cfg(not(bootstrap))] pub(crate) const SIGN_MASK: u16 = 0x8000; /// Exponent mask @@ -256,11 +252,9 @@ impl f16 { pub(crate) const MAN_MASK: u16 = 0x03ff; /// Minimum representable positive value (min subnormal) - #[cfg(not(bootstrap))] const TINY_BITS: u16 = 0x1; /// Minimum representable negative value (min negative subnormal) - #[cfg(not(bootstrap))] const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK; /// Returns `true` if this value is NaN. @@ -278,7 +272,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { @@ -289,7 +282,6 @@ impl f16 { // concerns about portability, so this implementation is for // private use internally. #[inline] - #[cfg(not(bootstrap))] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub(crate) const fn abs_private(self) -> f16 { // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. @@ -317,7 +309,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_infinite(self) -> bool { @@ -344,7 +335,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_finite(self) -> bool { @@ -377,7 +367,6 @@ impl f16 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_subnormal(self) -> bool { @@ -408,7 +397,6 @@ impl f16 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_normal(self) -> bool { @@ -433,7 +421,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn classify(self) -> FpCategory { @@ -478,7 +465,6 @@ impl f16 { /// but getting floats correct is important for not accidentally leaking const eval /// runtime-deviating logic which may or may not be acceptable. #[inline] - #[cfg(not(bootstrap))] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const unsafe fn partial_classify(self) -> FpCategory { // SAFETY: The caller is not asking questions for which this will tell lies. @@ -593,7 +579,6 @@ impl f16 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_up(self) -> Self { @@ -648,7 +633,6 @@ impl f16 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_down(self) -> Self { @@ -685,7 +669,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn recip(self) -> Self { @@ -706,7 +689,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_degrees(self) -> Self { @@ -730,7 +712,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_radians(self) -> f16 { @@ -739,6 +720,177 @@ impl f16 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f16) -> f16 { + intrinsics::maxnumf16(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f16) -> f16 { + intrinsics::minnumf16(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f16) -> f16 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f16) -> f16 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(num_midpoint)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert_eq!(1f16.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f16).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f16) -> f16 { + const LO: f16 = f16::MIN_POSITIVE * 2.; + const HI: f16 = f16::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve `a` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// @@ -1167,7 +1319,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i16; @@ -1226,7 +1377,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn clamp(mut self, min: f16, max: f16) -> f16 { diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 08d863f17caf7..e65c982b17227 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1070,13 +1070,13 @@ impl f32 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 5d33eea6d011f..b27d47b07d544 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1064,13 +1064,13 @@ impl f64 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index 751edd3c79383..f8db6370653ab 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -12,48 +12,51 @@ use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS}; static POW10: [Digit; 10] = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]; -static TWOPOW10: [Digit; 10] = - [2, 20, 200, 2000, 20000, 200000, 2000000, 20000000, 200000000, 2000000000]; - -// precalculated arrays of `Digit`s for 10^(2^n) -static POW10TO16: [Digit; 2] = [0x6fc10000, 0x2386f2]; -static POW10TO32: [Digit; 4] = [0, 0x85acef81, 0x2d6d415b, 0x4ee]; -static POW10TO64: [Digit; 7] = [0, 0, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03]; -static POW10TO128: [Digit; 14] = [ - 0, 0, 0, 0, 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, - 0xa6337f19, 0xe91f2603, 0x24e, +// precalculated arrays of `Digit`s for 5^(2^n). +static POW5TO16: [Digit; 2] = [0x86f26fc1, 0x23]; +static POW5TO32: [Digit; 3] = [0x85acef81, 0x2d6d415b, 0x4ee]; +static POW5TO64: [Digit; 5] = [0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03]; +static POW5TO128: [Digit; 10] = [ + 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, 0xa6337f19, + 0xe91f2603, 0x24e, ]; -static POW10TO256: [Digit; 27] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, - 0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 0x65f9ef17, - 0x55bc28f2, 0x80dcc7f7, 0xf46eeddc, 0x5fdcefce, 0x553f7, +static POW5TO256: [Digit; 19] = [ + 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, 0xd595d80f, 0x26b2716e, + 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, + 0xf46eeddc, 0x5fdcefce, 0x553f7, ]; #[doc(hidden)] pub fn mul_pow10(x: &mut Big, n: usize) -> &mut Big { debug_assert!(n < 512); + // Save ourself the left shift for the smallest cases. + if n < 8 { + return x.mul_small(POW10[n & 7]); + } + // Multiply by the powers of 5 and shift the 2s in at the end. + // This keeps the intermediate products smaller and faster. if n & 7 != 0 { - x.mul_small(POW10[n & 7]); + x.mul_small(POW10[n & 7] >> (n & 7)); } if n & 8 != 0 { - x.mul_small(POW10[8]); + x.mul_small(POW10[8] >> 8); } if n & 16 != 0 { - x.mul_digits(&POW10TO16); + x.mul_digits(&POW5TO16); } if n & 32 != 0 { - x.mul_digits(&POW10TO32); + x.mul_digits(&POW5TO32); } if n & 64 != 0 { - x.mul_digits(&POW10TO64); + x.mul_digits(&POW5TO64); } if n & 128 != 0 { - x.mul_digits(&POW10TO128); + x.mul_digits(&POW5TO128); } if n & 256 != 0 { - x.mul_digits(&POW10TO256); + x.mul_digits(&POW5TO256); } - x + x.mul_pow2(n) } fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big { @@ -62,7 +65,7 @@ fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big { x.div_rem_small(POW10[largest]); n -= largest; } - x.div_rem_small(TWOPOW10[n]); + x.div_rem_small(POW10[n] << 1); x } diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index 753f14c6b85ec..13df888d24c5c 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -76,7 +76,7 @@ pub trait Coroutine { /// values which are allowed to be returned each time a coroutine yields. /// For example an iterator-as-a-coroutine would likely have this type as /// `T`, the type being iterated over. - #[cfg_attr(not(bootstrap), lang = "coroutine_yield")] + #[lang = "coroutine_yield"] type Yield; /// The type of value this coroutine returns. @@ -85,7 +85,7 @@ pub trait Coroutine { /// `return` statement or implicitly as the last expression of a coroutine /// literal. For example futures would use this as `Result` as it /// represents a completed future. - #[cfg_attr(not(bootstrap), lang = "coroutine_return")] + #[lang = "coroutine_return"] type Return; /// Resumes the execution of this coroutine. diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index 36ae581e3f723..c6083a121d107 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -171,12 +171,13 @@ /// still be live when `T` gets dropped. The exact details of this analysis are not yet /// stably guaranteed and **subject to change**. Currently, the analysis works as follows: /// - If `T` has no drop glue, then trivially nothing is required to be live. This is the case if -/// neither `T` nor any of its (recursive) fields have a destructor (`impl Drop`). [`PhantomData`] -/// and [`ManuallyDrop`] are considered to never have a destructor, no matter their field type. +/// neither `T` nor any of its (recursive) fields have a destructor (`impl Drop`). [`PhantomData`], +/// arrays of length 0 and [`ManuallyDrop`] are considered to never have a destructor, no matter +/// their field type. /// - If `T` has drop glue, then, for all types `U` that are *owned* by any field of `T`, /// recursively add the types and lifetimes that need to be live when `U` gets dropped. The set of /// owned types is determined by recursively traversing `T`: -/// - Recursively descend through `PhantomData`, `Box`, tuples, and arrays (including arrays of +/// - Recursively descend through `PhantomData`, `Box`, tuples, and arrays (excluding arrays of /// length 0). /// - Stop at reference and raw pointer types as well as function pointers and function items; /// they do not own anything. diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 8e641e515fb75..6c5236ed99ce8 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -10,7 +10,7 @@ mod unwind_safe; pub use self::location::Location; #[stable(feature = "panic_hooks", since = "1.10.0")] pub use self::panic_info::PanicInfo; -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] pub use self::panic_info::PanicMessage; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 961ff6e091695..e4d0c897b65ca 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -24,7 +24,7 @@ pub struct PanicInfo<'a> { /// that were given to the `panic!()` macro. /// /// See [`PanicInfo::message`]. -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] pub struct PanicMessage<'a> { message: fmt::Arguments<'a>, } @@ -57,7 +57,7 @@ impl<'a> PanicInfo<'a> { /// } /// ``` #[must_use] - #[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "panic_info_message", since = "1.81.0")] pub fn message(&self) -> PanicMessage<'_> { PanicMessage { message: self.message } } @@ -164,7 +164,7 @@ impl<'a> PanicMessage<'a> { /// For most cases with placeholders, this function will return `None`. /// /// See [`fmt::Arguments::as_str`] for details. - #[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "panic_info_message", since = "1.81.0")] #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")] #[must_use] #[inline] @@ -173,7 +173,7 @@ impl<'a> PanicMessage<'a> { } } -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] impl Display for PanicMessage<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -181,7 +181,7 @@ impl Display for PanicMessage<'_> { } } -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] impl fmt::Debug for PanicMessage<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d752151d10cc8..0569b8b762433 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1715,10 +1715,56 @@ impl fmt::Pointer for Pin { // for other reasons, though, so we just need to take care not to allow such // impls to land in std. #[stable(feature = "pin", since = "1.33.0")] -impl CoerceUnsized> for Pin where Ptr: CoerceUnsized {} +impl CoerceUnsized> for Pin +where + Ptr: CoerceUnsized + PinCoerceUnsized, + U: PinCoerceUnsized, +{ +} + +#[stable(feature = "pin", since = "1.33.0")] +impl DispatchFromDyn> for Pin +where + Ptr: DispatchFromDyn + PinCoerceUnsized, + U: PinCoerceUnsized, +{ +} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +/// Trait that indicates that this is a pointer or a wrapper for one, where +/// unsizing can be performed on the pointee when it is pinned. +/// +/// # Safety +/// +/// If this type implements `Deref`, then the concrete type returned by `deref` +/// and `deref_mut` must not change without a modification. The following +/// operations are not considered modifications: +/// +/// * Moving the pointer. +/// * Performing unsizing coercions on the pointer. +/// * Performing dynamic dispatch with the pointer. +/// * Calling `deref` or `deref_mut` on the pointer. +/// +/// The concrete type of a trait object is the type that the vtable corresponds +/// to. The concrete type of a slice is an array of the same element type and +/// the length specified in the metadata. The concrete type of a sized type +/// is the type itself. +pub unsafe trait PinCoerceUnsized {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a T {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a mut T {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl PinCoerceUnsized for Pin {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl PinCoerceUnsized for *const T {} #[stable(feature = "pin", since = "1.33.0")] -impl DispatchFromDyn> for Pin where Ptr: DispatchFromDyn {} +unsafe impl PinCoerceUnsized for *mut T {} /// Constructs a [Pin]<[&mut] T>, by pinning a `value: T` locally. /// diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 5989bcbcc5201..09ebef89fb0c2 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1244,6 +1244,9 @@ mod prim_f64 {} /// actually implement it. For x86-64 and AArch64, ISA support is not even specified, /// so it will always be a software implementation significantly slower than `f64`. /// +/// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On +/// x86 in particular, these functions do link but their results are always incorrect._ +/// /// *[See also the `std::f128::consts` module](crate::f128::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 4a716a7503964..44db227b79efd 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -3,6 +3,7 @@ use crate::marker::Unsize; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; use crate::ub_checks::assert_unsafe_precondition; @@ -1724,6 +1725,9 @@ impl CoerceUnsized> for NonNull where T: Uns #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl DispatchFromDyn> for NonNull where T: Unsize {} +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl PinCoerceUnsized for NonNull {} + #[stable(feature = "nonnull", since = "1.25.0")] impl fmt::Debug for NonNull { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index b74d691e45427..4810ebe01f9bb 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -1,6 +1,7 @@ use crate::fmt; use crate::marker::{PhantomData, Unsize}; use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::NonNull; /// A wrapper around a raw non-null `*mut T` that indicates that the possessor @@ -166,6 +167,9 @@ impl CoerceUnsized> for Unique where T: Unsiz #[unstable(feature = "ptr_internals", issue = "none")] impl DispatchFromDyn> for Unique where T: Unsize {} +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Unique {} + #[unstable(feature = "ptr_internals", issue = "none")] impl fmt::Debug for Unique { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 0b0a416ea2439..b1440214d795a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -7,7 +7,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::intrinsics::{exact_div, unchecked_sub}; +use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; @@ -522,7 +522,7 @@ impl [T] { /// ``` #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] - #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] + #[rustc_const_stable(feature = "const_slice_last_chunk", since = "1.80.0")] pub const fn last_chunk(&self) -> Option<&[T; N]> { if self.len() < N { None @@ -2770,41 +2770,54 @@ impl [T] { where F: FnMut(&'a T) -> Ordering, { - // INVARIANTS: - // - 0 <= left <= left + size = right <= self.len() - // - f returns Less for everything in self[..left] - // - f returns Greater for everything in self[right..] let mut size = self.len(); - let mut left = 0; - let mut right = size; - while left < right { - let mid = left + size / 2; - - // SAFETY: the while condition means `size` is strictly positive, so - // `size/2 < size`. Thus `left + size/2 < left + size`, which - // coupled with the `left + size <= self.len()` invariant means - // we have `left + size/2 < self.len()`, and this is in-bounds. + if size == 0 { + return Err(0); + } + let mut base = 0usize; + + // This loop intentionally doesn't have an early exit if the comparison + // returns Equal. We want the number of loop iterations to depend *only* + // on the size of the input slice so that the CPU can reliably predict + // the loop count. + while size > 1 { + let half = size / 2; + let mid = base + half; + + // SAFETY: the call is made safe by the following inconstants: + // - `mid >= 0`: by definition + // - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...` let cmp = f(unsafe { self.get_unchecked(mid) }); - // This control flow produces conditional moves, which results in - // fewer branches and instructions than if/else or matching on - // cmp::Ordering. - // This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx. - left = if cmp == Less { mid + 1 } else { left }; - right = if cmp == Greater { mid } else { right }; - if cmp == Equal { - // SAFETY: same as the `get_unchecked` above - unsafe { hint::assert_unchecked(mid < self.len()) }; - return Ok(mid); - } - - size = right - left; + // Binary search interacts poorly with branch prediction, so force + // the compiler to use conditional moves if supported by the target + // architecture. + base = select_unpredictable(cmp == Greater, base, mid); + + // This is imprecise in the case where `size` is odd and the + // comparison returns Greater: the mid element still gets included + // by `size` even though it's known to be larger than the element + // being searched for. + // + // This is fine though: we gain more performance by keeping the + // loop iteration count invariant (and thus predictable) than we + // lose from considering one additional element. + size -= half; } - // SAFETY: directly true from the overall invariant. - // Note that this is `<=`, unlike the assume in the `Ok` path. - unsafe { hint::assert_unchecked(left <= self.len()) }; - Err(left) + // SAFETY: base is always in [0, size) because base <= mid. + let cmp = f(unsafe { self.get_unchecked(base) }); + if cmp == Equal { + // SAFETY: same as the `get_unchecked` above. + unsafe { hint::assert_unchecked(base < self.len()) }; + Ok(base) + } else { + let result = base + (cmp == Less) as usize; + // SAFETY: same as the `get_unchecked` above. + // Note that this is `<=`, unlike the assume in the `Ok` path. + unsafe { hint::assert_unchecked(result <= self.len()) }; + Err(result) + } } /// Binary searches this slice with a key extraction function. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 912f164bcbd14..495d9191a9f85 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1077,7 +1077,7 @@ impl AtomicBool { /// assert_eq!(foo.load(Ordering::SeqCst), true); /// ``` #[inline] - #[stable(feature = "atomic_bool_fetch_not", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "atomic_bool_fetch_not", since = "1.81.0")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_not(&self, order: Ordering) -> bool { diff --git a/library/core/src/time.rs b/library/core/src/time.rs index e2693a53bbd8e..0390bb59a8984 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -617,16 +617,14 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// /// assert_eq!(Duration::new(100, 0).abs_diff(Duration::new(80, 0)), Duration::new(20, 0)); /// assert_eq!(Duration::new(100, 400_000_000).abs_diff(Duration::new(110, 0)), Duration::new(9, 600_000_000)); /// ``` - #[stable(feature = "duration_abs_diff", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "duration_abs_diff", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_abs_diff", since = "1.81.0")] + #[rustc_const_stable(feature = "duration_abs_diff", since = "1.81.0")] #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -640,8 +638,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -700,8 +696,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -758,8 +752,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -814,8 +806,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// diff --git a/library/core/tests/pin.rs b/library/core/tests/pin.rs index 6f617c8d0c297..7a6af46a74323 100644 --- a/library/core/tests/pin.rs +++ b/library/core/tests/pin.rs @@ -29,3 +29,49 @@ fn pin_const() { pin_mut_const(); } + +#[allow(unused)] +mod pin_coerce_unsized { + use core::cell::{Cell, RefCell, UnsafeCell}; + use core::pin::Pin; + use core::ptr::NonNull; + + pub trait MyTrait {} + impl MyTrait for String {} + + // These Pins should continue to compile. + // Do note that these instances of Pin types cannot be used + // meaningfully because all methods require a Deref/DerefMut + // bounds on the pointer type and Cell, RefCell and UnsafeCell + // do not implement Deref/DerefMut. + + pub fn cell(arg: Pin>>) -> Pin>> { + arg + } + pub fn ref_cell(arg: Pin>>) -> Pin>> { + arg + } + pub fn unsafe_cell(arg: Pin>>) -> Pin>> { + arg + } + + // These sensible Pin coercions are possible. + pub fn pin_mut_ref(arg: Pin<&mut String>) -> Pin<&mut dyn MyTrait> { + arg + } + pub fn pin_ref(arg: Pin<&String>) -> Pin<&dyn MyTrait> { + arg + } + pub fn pin_ptr(arg: Pin<*const String>) -> Pin<*const dyn MyTrait> { + arg + } + pub fn pin_ptr_mut(arg: Pin<*mut String>) -> Pin<*mut dyn MyTrait> { + arg + } + pub fn pin_non_null(arg: Pin>) -> Pin> { + arg + } + pub fn nesting_pins(arg: Pin>) -> Pin> { + arg + } +} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index acabedcdc2668..cdefb5d3eb201 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -69,13 +69,13 @@ fn test_binary_search() { assert_eq!(b.binary_search(&8), Err(5)); let b = [(); usize::MAX]; - assert_eq!(b.binary_search(&()), Ok(usize::MAX / 2)); + assert_eq!(b.binary_search(&()), Ok(usize::MAX - 1)); } #[test] fn test_binary_search_by_overflow() { let b = [(); usize::MAX]; - assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX / 2)); + assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX - 1)); assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0)); assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX)); } @@ -87,13 +87,13 @@ fn test_binary_search_implementation_details() { let b = [1, 1, 2, 2, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(1)); assert_eq!(b.binary_search(&2), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(5)); + assert_eq!(b.binary_search(&3), Ok(6)); let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(4)); - assert_eq!(b.binary_search(&3), Ok(7)); + assert_eq!(b.binary_search(&3), Ok(8)); let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(2)); - assert_eq!(b.binary_search(&3), Ok(4)); + assert_eq!(b.binary_search(&1), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(8)); } #[test] diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index 14ba4af2bb575..dc2b42bb90ae8 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -14,7 +14,6 @@ #![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] -#![cfg_attr(bootstrap, feature(c_unwind))] #![allow(internal_features)] #[cfg(target_os = "android")] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 77abb9125f651..2d174f4b1a4a2 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -24,7 +24,6 @@ #![feature(rustc_attrs)] #![panic_runtime] #![feature(panic_runtime)] -#![cfg_attr(bootstrap, feature(c_unwind))] // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] diff --git a/library/std/build.rs b/library/std/build.rs index c542ba81eedc1..18ca7b512a9b6 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -85,6 +85,11 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + // This is a step beyond only having the types and basic functions available. Math functions + // aren't consistently available or correct. + println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)"); + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { // Selection failure until recent LLVM // FIXME(llvm19): can probably be removed at the version bump @@ -94,7 +99,7 @@ fn main() { // Unsupported ("arm64ec", _) => false, // MinGW ABI bugs - ("x86", "windows") => false, + ("x86_64", "windows") => false, // x86 has ABI bugs that show up with optimizations. This should be partially fixed with // the compiler-builtins update. ("x86" | "x86_64", _) => false, @@ -122,16 +127,50 @@ fn main() { ("nvptx64", _) => false, // ABI unsupported ("sparc", _) => false, + // MinGW ABI bugs + ("x86_64", "windows") => false, // 64-bit Linux is about the only platform to have f128 symbols by default (_, "linux") if target_pointer_width == 64 => true, // Same as for f16, except MacOS is also missing f128 symbols. _ => false, }; + // These are currently empty, but will fill up as some platforms move from completely + // unreliable to reliable basics but unreliable math. + + // LLVM is currenlty adding missing routines, + let has_reliable_f16_math = has_reliable_f16 + && match (target_arch.as_str(), target_os.as_str()) { + // Currently nothing special. Hooray! + // This will change as platforms gain better better support for standard ops but math + // lags behind. + _ => true, + }; + + let has_reliable_f128_math = has_reliable_f128 + && match (target_arch.as_str(), target_os.as_str()) { + // LLVM lowers `fp128` math to `long double` symbols even on platforms where + // `long double` is not IEEE binary128. See + // . + // + // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits + // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` + // (ld is 80-bit extended precision). + ("x86_64", _) => false, + (_, "linux") if target_pointer_width == 64 => true, + _ => false, + }; + if has_reliable_f16 { println!("cargo:rustc-cfg=reliable_f16"); } if has_reliable_f128 { println!("cargo:rustc-cfg=reliable_f128"); } + if has_reliable_f16_math { + println!("cargo:rustc-cfg=reliable_f16_math"); + } + if has_reliable_f128_math { + println!("cargo:rustc-cfg=reliable_f128_math"); + } } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index f5905605e7887..3e17431af45b0 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -500,13 +500,8 @@ where } if self.show_backtrace { - let backtrace = self.backtrace(); - - if let Some(backtrace) = backtrace { - let backtrace = backtrace.to_string(); - - f.write_str("\n\nStack backtrace:\n")?; - f.write_str(backtrace.trim_end())?; + if let Some(backtrace) = self.backtrace() { + write!(f, "\n\nStack backtrace:\n{}", backtrace.to_string().trim_end())?; } } diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 34817fe6ae583..f6df6259137bf 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -12,25 +12,180 @@ pub use core::f128::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } + pub fn floor(self) -> f128 { + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + unsafe { intrinsics::rintf128(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -41,7 +196,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// # #[cfg(reliable_f128)] { /// /// let x = 3.5_f128; /// let y = -3.5_f128; @@ -53,7 +208,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] @@ -62,4 +216,1129 @@ impl f128 { // We don't do this now because LLVM has lowering bugs for f128 math. Self::from_bits(self.to_bits() & !(1 << 127)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f128::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f128::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f128 { + if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f128) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f128); + /// assert_eq!(f.copysign(-0.42), -3.5_f128); + /// assert_eq!((-f).copysign(0.42), 3.5_f128); + /// assert_eq!((-f).copysign(-0.42), -3.5_f128); + /// + /// assert!(f128::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f128) -> f128 { + unsafe { intrinsics::copysignf128(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + unsafe { intrinsics::powif128(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f128) -> f128 { + unsafe { intrinsics::powf128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + unsafe { intrinsics::sqrtf128(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f128 { + unsafe { intrinsics::expf128(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 2.0f128; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f128 { + unsafe { intrinsics::exp2f128(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f128 { + unsafe { intrinsics::logf128(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let five = 5.0f128; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f128) -> f128 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let two = 2.0f128; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f128 { + unsafe { intrinsics::log2f128(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let ten = 10.0f128; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f128 { + unsafe { intrinsics::log10f128(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `cbrtf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 8.0f128; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f128 { + unsafe { cmath::cbrtf128(self) } + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `hypotf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// let y = 3.0f128; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f128) -> f128 { + unsafe { cmath::hypotf128(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f128 { + unsafe { intrinsics::sinf128(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0 * std::f128::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f128 { + unsafe { intrinsics::cosf128(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf128` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f128 { + unsafe { cmath::tanf128(self) } + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f128::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f128 { + unsafe { cmath::asinf128(self) } + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f128::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f128 { + unsafe { cmath::acosf128(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 1.0f128; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f128 { + unsafe { cmath::atanf128(self) } + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f128; + /// let y1 = -3.0f128; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f128; + /// let y2 = 3.0f128; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f128::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f128::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f128::EPSILON); + /// assert!(abs_difference_2 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f128) -> f128 { + unsafe { cmath::atan2f128(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f128::sin(x), + /// f128::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f128::EPSILON); + /// assert!(abs_difference_1 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn sin_cos(self) -> (f128, f128) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f128 { + unsafe { cmath::expm1f128(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn ln_1p(self) -> f128 { + unsafe { cmath::log1pf128(self) } + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f128 { + unsafe { cmath::sinhf128(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f128 { + unsafe { cmath::coshf128(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f128 { + unsafe { cmath::tanhf128(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f128 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f128 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 1e-5); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f128 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 5.0f128; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f128 { + unsafe { cmath::tgammaf128(self) } + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgammaf128_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f128, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index dd42da8db300d..7051c051bf723 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -1,10 +1,24 @@ -#![cfg(not(bootstrap))] // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f128)] use crate::f128::consts; use crate::num::{FpCategory as Fp, *}; +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; + /// Smallest number const TINY_BITS: u128 = 0x1; @@ -42,7 +56,33 @@ fn test_num_f128() { test_num(10f128, 2f128); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} #[test] fn test_nan() { @@ -192,9 +232,100 @@ fn test_classify() { assert_eq!(1e-4932f128.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} #[test] +#[cfg(reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] fn test_abs() { assert_eq!(f128::INFINITY.abs(), f128::INFINITY); assert_eq!(1f128.abs(), 1f128); @@ -294,6 +425,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -302,11 +451,161 @@ fn test_recip() { assert_eq!(2.0f128.recip(), 0.5); assert_eq!((-0.4f128).recip(), -2.5); assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f128 = consts::PI; @@ -314,8 +613,8 @@ fn test_to_degrees() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -329,19 +628,122 @@ fn test_to_radians() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.698279); - assert_approx_eq!((-332.31f128).to_radians(), -5.799903); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); // check approx rather than exact because round trip for pi doesn't fall on an exactly // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi); + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { - // FIXME(f16_f128): add math tests when available use super::consts; let pi: f128 = consts::PI; @@ -352,29 +754,34 @@ fn test_real_consts() { let frac_pi_8: f128 = consts::FRAC_PI_8; let frac_1_pi: f128 = consts::FRAC_1_PI; let frac_2_pi: f128 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f128 = consts::SQRT_2; - // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - // let e: f128 = consts::E; - // let log2_e: f128 = consts::LOG2_E; - // let log10_e: f128 = consts::LOG10_E; - // let ln_2: f128 = consts::LN_2; - // let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f128); - assert_approx_eq!(frac_pi_3, pi / 3f128); - assert_approx_eq!(frac_pi_4, pi / 4f128); - assert_approx_eq!(frac_pi_6, pi / 6f128); - assert_approx_eq!(frac_pi_8, pi / 8f128); - assert_approx_eq!(frac_1_pi, 1f128 / pi); - assert_approx_eq!(frac_2_pi, 2f128 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f128.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f128.ln()); - // assert_approx_eq!(ln_10, 10f128.ln()); + + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(reliable_f128_math)] + { + let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + let sqrt2: f128 = consts::SQRT_2; + let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + let e: f128 = consts::E; + let log2_e: f128 = consts::LOG2_E; + let log10_e: f128 = consts::LOG10_E; + let ln_2: f128 = consts::LN_2; + let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } } #[test] @@ -383,10 +790,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index bd5328d335640..10908332762d5 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -12,25 +12,180 @@ pub use core::f16::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } + pub fn floor(self) -> f16 { + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + unsafe { intrinsics::rintf16(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -53,7 +208,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] @@ -61,4 +215,1127 @@ impl f16 { // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available Self::from_bits(self.to_bits() & !(1 << 15)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f16::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f16::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f16 { + if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f16) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f16); + /// assert_eq!(f.copysign(-0.42), -3.5_f16); + /// assert_eq!((-f).copysign(0.42), 3.5_f16); + /// assert_eq!((-f).copysign(-0.42), -3.5_f16); + /// + /// assert!(f16::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f16) -> f16 { + unsafe { intrinsics::copysignf16(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + unsafe { intrinsics::powif16(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f16) -> f16 { + unsafe { intrinsics::powf16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f16 { + unsafe { intrinsics::expf16(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 2.0f16; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f16 { + unsafe { intrinsics::exp2f16(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f16 { + unsafe { intrinsics::logf16(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let five = 5.0f16; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f16) -> f16 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let two = 2.0f16; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f16 { + unsafe { intrinsics::log2f16(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let ten = 10.0f16; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f16 { + unsafe { intrinsics::log10f16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + (unsafe { cmath::cbrtf(self as f32) }) as f16 + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `hypotf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// let y = 3.0f16; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f16) -> f16 { + (unsafe { cmath::hypotf(self as f32, other as f32) }) as f16 + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f16 { + unsafe { intrinsics::sinf16(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0 * std::f16::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f16 { + unsafe { intrinsics::cosf16(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f16 { + (unsafe { cmath::tanf(self as f32) }) as f16 + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f16::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f16 { + (unsafe { cmath::asinf(self as f32) }) as f16 + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f16::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f16 { + (unsafe { cmath::acosf(self as f32) }) as f16 + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 1.0f16; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f16 { + (unsafe { cmath::atanf(self as f32) }) as f16 + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f16; + /// let y1 = -3.0f16; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f16; + /// let y2 = 3.0f16; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f16::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f16::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f16::EPSILON); + /// assert!(abs_difference_2 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f16) -> f16 { + (unsafe { cmath::atan2f(self as f32, other as f32) }) as f16 + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f16::sin(x), + /// f16::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f16::EPSILON); + /// assert!(abs_difference_1 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + pub fn sin_cos(self) -> (f16, f16) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f16 { + (unsafe { cmath::expm1f(self as f32) }) as f16 + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_1p(self) -> f16 { + (unsafe { cmath::log1pf(self as f32) }) as f16 + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f16 { + (unsafe { cmath::sinhf(self as f32) }) as f16 + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f16 { + (unsafe { cmath::coshf(self as f32) }) as f16 + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f16 { + (unsafe { cmath::tanhf(self as f32) }) as f16 + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f16 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f16 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f16 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 5.0f16; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f16 { + (unsafe { cmath::tgammaf(self as f32) }) as f16 + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f16, i32) { + let mut signgamp: i32 = 0; + let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; + (x, signgamp) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index d9b9d99bf04b3..50504e7ffd94f 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -1,15 +1,24 @@ -#![cfg(not(bootstrap))] // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f16)] use crate::f16::consts; use crate::num::{FpCategory as Fp, *}; -// We run out of precision pretty quickly with f16 -// const F16_APPROX_L1: f16 = 0.001; -const F16_APPROX_L2: f16 = 0.01; -// const F16_APPROX_L3: f16 = 0.1; -const F16_APPROX_L4: f16 = 0.5; +/// Tolerance for results on the order of 10.0e-2; +#[cfg(reliable_f16_math)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[cfg(reliable_f16_math)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[cfg(reliable_f16_math)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[cfg(reliable_f16_math)] +const TOL_P4: f16 = 10.0; /// Smallest number const TINY_BITS: u16 = 0x1; @@ -48,7 +57,33 @@ fn test_num_f16() { test_num(10f16, 2f16); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} #[test] fn test_nan() { @@ -198,9 +233,100 @@ fn test_classify() { assert_eq!(1e-5f16.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} #[test] +#[cfg(reliable_f16_math)] fn test_abs() { assert_eq!(f16::INFINITY.abs(), f16::INFINITY); assert_eq!(1f16.abs(), 1f16); @@ -300,6 +426,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -308,11 +452,157 @@ fn test_recip() { assert_eq!(2.0f16.recip(), 0.5); assert_eq!((-0.4f16).recip(), -2.5); assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +#[test] +#[cfg(reliable_f16_math)] +fn test_powi() { + // FIXME(llvm19): LLVM misoptimizes `powi.f16` + // + // let nan: f16 = f16::NAN; + // let inf: f16 = f16::INFINITY; + // let neg_inf: f16 = f16::NEG_INFINITY; + // assert_eq!(1.0f16.powi(1), 1.0); + // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + // assert_eq!(8.3f16.powi(0), 1.0); + // assert!(nan.powi(2).is_nan()); + // assert_eq!(inf.powi(3), inf); + // assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f16 = consts::PI; @@ -320,8 +610,8 @@ fn test_to_degrees() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521); - assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -335,14 +625,112 @@ fn test_to_radians() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903); - assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available @@ -356,29 +744,34 @@ fn test_real_consts() { let frac_pi_8: f16 = consts::FRAC_PI_8; let frac_1_pi: f16 = consts::FRAC_1_PI; let frac_2_pi: f16 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f16 = consts::SQRT_2; - // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - // let e: f16 = consts::E; - // let log2_e: f16 = consts::LOG2_E; - // let log10_e: f16 = consts::LOG10_E; - // let ln_2: f16 = consts::LN_2; - // let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f16); - assert_approx_eq!(frac_pi_3, pi / 3f16); - assert_approx_eq!(frac_pi_4, pi / 4f16); - assert_approx_eq!(frac_pi_6, pi / 6f16); - assert_approx_eq!(frac_pi_8, pi / 8f16); - assert_approx_eq!(frac_1_pi, 1f16 / pi); - assert_approx_eq!(frac_2_pi, 2f16 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f16.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f16.ln()); - // assert_approx_eq!(ln_10, 10f16.ln()); + + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(reliable_f16_math)] + { + let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + let sqrt2: f16 = consts::SQRT_2; + let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + let e: f16 = consts::E; + let log2_e: f16 = consts::LOG2_E; + let log10_e: f16 = consts::LOG10_E; + let ln_2: f16 = consts::LN_2; + let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } } #[test] @@ -387,10 +780,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f16).to_bits(), 0x4a40); assert_eq!((1337f16).to_bits(), 0x6539); assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index b3afeca1ed855..12433d25bfa45 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -574,7 +574,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - crate::sys::log2f32(self) + unsafe { intrinsics::log2f32(self) } } /// Returns the base 10 logarithm of the number. diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index c8a709c7768db..a343e19173e59 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -574,7 +574,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - crate::sys::log2f64(self) + unsafe { intrinsics::log2f64(self) } } /// Returns the base 10 logarithm of the number. diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 65dbf43a42eaf..c5edb03bb08be 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2744,7 +2744,7 @@ impl AsInnerMut for DirBuilder { /// ``` /// /// [`Path::exists`]: crate::path::Path::exists -#[stable(feature = "fs_try_exists", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fs_try_exists", since = "1.81.0")] #[inline] pub fn exists>(path: P) -> io::Result { fs_imp::exists(path.as_ref()) diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index f11dd50c5e2b7..0b12e5777c840 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -94,6 +94,40 @@ impl BufReader { pub fn with_capacity(capacity: usize, inner: R) -> BufReader { BufReader { inner, buf: Buffer::with_capacity(capacity) } } + + /// Attempt to look ahead `n` bytes. + /// + /// `n` must be less than `capacity`. + /// + /// ## Examples + /// + /// ```rust + /// #![feature(bufreader_peek)] + /// use std::io::{Read, BufReader}; + /// + /// let mut bytes = &b"oh, hello"[..]; + /// let mut rdr = BufReader::with_capacity(6, &mut bytes); + /// assert_eq!(rdr.peek(2).unwrap(), b"oh"); + /// let mut buf = [0; 4]; + /// rdr.read(&mut buf[..]).unwrap(); + /// assert_eq!(&buf, b"oh, "); + /// assert_eq!(rdr.peek(2).unwrap(), b"he"); + /// let mut s = String::new(); + /// rdr.read_to_string(&mut s).unwrap(); + /// assert_eq!(&s, "hello"); + /// ``` + #[unstable(feature = "bufreader_peek", issue = "128405")] + pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { + assert!(n <= self.capacity()); + while n > self.buf.buffer().len() { + if self.buf.pos() > 0 { + self.buf.backshift(); + } + self.buf.read_more(&mut self.inner)?; + debug_assert_eq!(self.buf.pos(), 0); + } + Ok(&self.buf.buffer()[..n]) + } } impl BufReader { diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 796137c0123e7..ccd67fafb45b4 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -97,6 +97,27 @@ impl Buffer { self.pos = self.pos.saturating_sub(amt); } + /// Read more bytes into the buffer without discarding any of its contents + pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> { + let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); + let old_init = self.initialized - self.pos; + unsafe { + buf.set_init(old_init); + } + reader.read_buf(buf.unfilled())?; + self.filled += buf.len(); + self.initialized += buf.init_len() - old_init; + Ok(()) + } + + /// Remove bytes that have already been read from the buffer. + pub fn backshift(&mut self) { + self.buf.copy_within(self.pos.., 0); + self.initialized -= self.pos; + self.filled -= self.pos; + self.pos = 0; + } + #[inline] pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 63338c934b505..644b294db8da1 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1262,7 +1262,7 @@ impl<'a> IoSliceMut<'a> { /// buf.advance(3); /// assert_eq!(buf.deref(), [1; 5].as_ref()); /// ``` - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance(&mut self, n: usize) { self.0.advance(n) @@ -1301,7 +1301,7 @@ impl<'a> IoSliceMut<'a> { /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); /// ``` - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { // Number of buffers to remove. @@ -1402,7 +1402,7 @@ impl<'a> IoSlice<'a> { /// buf.advance(3); /// assert_eq!(buf.deref(), [1; 5].as_ref()); /// ``` - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance(&mut self, n: usize) { self.0.advance(n) @@ -1440,7 +1440,7 @@ impl<'a> IoSlice<'a> { /// IoSlice::advance_slices(&mut bufs, 10); /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { // Number of buffers to remove. diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index c82228fca4bcf..9f4d244b5479e 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -155,7 +155,7 @@ mod break_keyword {} /// const WORDS: &str = "hello convenience!"; /// ``` /// -/// `const` items looks remarkably similar to `static` items, which introduces some confusion as +/// `const` items look remarkably similar to `static` items, which introduces some confusion as /// to which one should be used at which times. To put it simply, constants are inlined wherever /// they're used, making using them identical to simply replacing the name of the `const` with its /// value. Static variables, on the other hand, point to a single location in memory, which all diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ee6f5a6f3c0d5..67405d788bd9b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -269,14 +269,9 @@ #![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] -#![cfg_attr( - all(any(target_arch = "x86_64", target_arch = "x86"), target_os = "uefi"), - feature(stdarch_x86_has_cpuid) -)] // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -303,6 +298,7 @@ #![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] +#![feature(macro_metavar_expr_concat)] #![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(must_not_suspend)] @@ -343,6 +339,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] +#![feature(pin_coerce_unsized_trait)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(prelude_2024)] @@ -671,7 +668,6 @@ mod panicking; #[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; -// Re-export macros defined in core. #[unstable(feature = "cfg_match", issue = "115585")] pub use core::cfg_match; #[unstable( @@ -690,6 +686,7 @@ pub use core::{ env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, stringify, trace_macros, }; +// Re-export macros defined in core. #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::{ diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index ba519afc62b07..1b0d7f3dbf2c9 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -382,7 +382,7 @@ macro_rules! assert_approx_eq { let diff = (*a - *b).abs(); assert!( diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", lim = $lim ); }}; diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 1e6ecd7d7a67a..0d99d5492a268 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -138,6 +138,7 @@ pub trait IntoRawFd { /// let raw_fd: RawFd = f.into_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` + #[must_use = "losing the raw file descriptor may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_fd(self) -> RawFd; } diff --git a/library/std/src/os/hermit/mod.rs b/library/std/src/os/hermit/mod.rs index 02a4b2c3ab5e7..5812206a25759 100644 --- a/library/std/src/os/hermit/mod.rs +++ b/library/std/src/os/hermit/mod.rs @@ -1,4 +1,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#![deny(unsafe_op_in_unsafe_fn)] #[allow(unused_extern_crates)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 9e89d9fbc1b81..2d18f33961506 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -342,6 +342,7 @@ pub trait IntoRawFd { /// This function **transfers ownership** of the underlying file descriptor /// to the caller. Callers are then the unique owners of the file descriptor /// and must close the descriptor once it's no longer needed. + #[must_use = "losing the raw file descriptor may leak resources"] fn into_raw_fd(self) -> RawFd; } diff --git a/library/std/src/os/vxworks/mod.rs b/library/std/src/os/vxworks/mod.rs index 0a7ac641dd3e1..b09aa72f72693 100644 --- a/library/std/src/os/vxworks/mod.rs +++ b/library/std/src/os/vxworks/mod.rs @@ -1,6 +1,7 @@ //! VxWorks-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 4ba07e3d2afa5..6658248d574c9 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -85,6 +85,7 @@ pub trait IntoRawHandle { /// However, transferring ownership is not strictly required. Use a /// `Into::into` implementation for an API which strictly /// transfers ownership. + #[must_use = "losing the raw handle may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_handle(self) -> RawHandle; } @@ -228,6 +229,7 @@ pub trait IntoRawSocket { /// However, transferring ownership is not strictly required. Use a /// `Into::into` implementation for an API which strictly /// transfers ownership. + #[must_use = "losing the raw socket may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_socket(self) -> RawSocket; } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 57ba7c6d407fc..4c496ade81cda 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -37,7 +37,7 @@ pub type PanicInfo<'a> = PanicHookInfo<'a>; /// ``` /// /// [`set_hook`]: ../../std/panic/fn.set_hook.html -#[stable(feature = "panic_hook_info", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_hook_info", since = "1.81.0")] #[derive(Debug)] pub struct PanicHookInfo<'a> { payload: &'a (dyn Any + Send), @@ -463,11 +463,10 @@ static SHOULD_CAPTURE: AtomicU8 = AtomicU8::new(0); /// environment variable; see the details in [`get_backtrace_style`]. #[unstable(feature = "panic_backtrace_config", issue = "93346")] pub fn set_backtrace_style(style: BacktraceStyle) { - if !cfg!(feature = "backtrace") { - // If the `backtrace` feature of this crate isn't enabled, skip setting. - return; + if cfg!(feature = "backtrace") { + // If the `backtrace` feature of this crate is enabled, set the backtrace style. + SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release); } - SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release); } /// Checks whether the standard library's panic hook will capture and print a @@ -503,21 +502,13 @@ pub fn get_backtrace_style() -> Option { return Some(style); } - let format = crate::env::var_os("RUST_BACKTRACE") - .map(|x| { - if &x == "0" { - BacktraceStyle::Off - } else if &x == "full" { - BacktraceStyle::Full - } else { - BacktraceStyle::Short - } - }) - .unwrap_or(if crate::sys::FULL_BACKTRACE_DEFAULT { - BacktraceStyle::Full - } else { - BacktraceStyle::Off - }); + let format = match crate::env::var_os("RUST_BACKTRACE") { + Some(x) if &x == "0" => BacktraceStyle::Off, + Some(x) if &x == "full" => BacktraceStyle::Full, + Some(_) => BacktraceStyle::Short, + None if crate::sys::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full, + None => BacktraceStyle::Off, + }; set_backtrace_style(format); Some(format) } diff --git a/library/std/src/sys/anonymous_pipe/tests.rs b/library/std/src/pipe/tests.rs similarity index 90% rename from library/std/src/sys/anonymous_pipe/tests.rs rename to library/std/src/pipe/tests.rs index 865d24ec85596..9c38e10678752 100644 --- a/library/std/src/sys/anonymous_pipe/tests.rs +++ b/library/std/src/pipe/tests.rs @@ -2,6 +2,7 @@ use crate::io::{Read, Write}; use crate::pipe::pipe; #[test] +#[cfg(all(windows, unix, not(miri)))] fn pipe_creation_clone_and_rw() { let (rx, tx) = pipe().unwrap(); diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 9d969af8c6d84..bf595fdea2d25 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -70,7 +70,7 @@ pub(crate) enum ExclusiveState { #[stable(feature = "rust1", since = "1.0.0")] #[deprecated( since = "1.38.0", - note = "the `new` function is now preferred", + note = "the `Once::new()` function is now preferred", suggestion = "Once::new()" )] pub const ONCE_INIT: Once = Once::new(); @@ -264,6 +264,47 @@ impl Once { self.inner.is_completed() } + /// Blocks the current thread until initialization has completed. + /// + /// # Example + /// + /// ```rust + /// #![feature(once_wait)] + /// + /// use std::sync::Once; + /// use std::thread; + /// + /// static READY: Once = Once::new(); + /// + /// let thread = thread::spawn(|| { + /// READY.wait(); + /// println!("everything is ready"); + /// }); + /// + /// READY.call_once(|| println!("performing setup")); + /// ``` + /// + /// # Panics + /// + /// If this [`Once`] has been poisoned because an initialization closure has + /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) + /// if this behaviour is not desired. + #[unstable(feature = "once_wait", issue = "127527")] + pub fn wait(&self) { + if !self.inner.is_completed() { + self.inner.wait(false); + } + } + + /// Blocks the current thread until initialization has completed, ignoring + /// poisoning. + #[unstable(feature = "once_wait", issue = "127527")] + pub fn wait_force(&self) { + if !self.inner.is_completed() { + self.inner.wait(true); + } + } + /// Returns the current state of the `Once` instance. /// /// Since this takes a mutable reference, no initialization can currently diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/once/tests.rs index d43dabc1cf137..ce96468aeb6e1 100644 --- a/library/std/src/sync/once/tests.rs +++ b/library/std/src/sync/once/tests.rs @@ -1,5 +1,8 @@ use super::Once; +use crate::sync::atomic::AtomicBool; +use crate::sync::atomic::Ordering::Relaxed; use crate::sync::mpsc::channel; +use crate::time::Duration; use crate::{panic, thread}; #[test] @@ -113,3 +116,47 @@ fn wait_for_force_to_finish() { assert!(t1.join().is_ok()); assert!(t2.join().is_ok()); } + +#[test] +fn wait() { + for _ in 0..50 { + let val = AtomicBool::new(false); + let once = Once::new(); + + thread::scope(|s| { + for _ in 0..4 { + s.spawn(|| { + once.wait(); + assert!(val.load(Relaxed)); + }); + } + + once.call_once(|| val.store(true, Relaxed)); + }); + } +} + +#[test] +fn wait_on_poisoned() { + let once = Once::new(); + + panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); + panic::catch_unwind(|| once.wait()).unwrap_err(); +} + +#[test] +fn wait_force_on_poisoned() { + let once = Once::new(); + + thread::scope(|s| { + panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); + + s.spawn(|| { + thread::sleep(Duration::from_millis(100)); + + once.call_once_force(|_| {}); + }); + + once.wait_force(); + }) +} diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 60e43a1cde10e..56cf877ddc6d5 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -167,6 +167,34 @@ impl OnceLock { } } + /// Blocks the current thread until the cell is initialized. + /// + /// # Example + /// + /// Waiting for a computation on another thread to finish: + /// ```rust + /// #![feature(once_wait)] + /// + /// use std::thread; + /// use std::sync::OnceLock; + /// + /// let value = OnceLock::new(); + /// + /// thread::scope(|s| { + /// s.spawn(|| value.set(1 + 1)); + /// + /// let result = value.wait(); + /// assert_eq!(result, &2); + /// }) + /// ``` + #[inline] + #[unstable(feature = "once_wait", issue = "127527")] + pub fn wait(&self) -> &T { + self.once.wait_force(); + + unsafe { self.get_unchecked() } + } + /// Sets the contents of this cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is @@ -281,9 +309,7 @@ impl OnceLock { /// Gets the mutable reference of the contents of the cell, initializing /// it with `f` if the cell was empty. /// - /// Many threads may call `get_mut_or_init` concurrently with different - /// initializing functions, but it is guaranteed that only one function - /// will be executed. + /// This method never blocks. /// /// # Panics /// @@ -373,6 +399,8 @@ impl OnceLock { /// it with `f` if the cell was empty. If the cell was empty and `f` failed, /// an error is returned. /// + /// This method never blocks. + /// /// # Panics /// /// If `f` panics, the panic is propagated to the caller, and diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs index 74875677cf3e7..aa14c8b650d34 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -1,18 +1,14 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + cfg_if::cfg_if! { if #[cfg(unix)] { mod unix; - pub(crate) use unix::{AnonPipe, pipe}; - - #[cfg(all(test, not(miri)))] - mod tests; + pub use unix::{AnonPipe, pipe}; } else if #[cfg(windows)] { mod windows; - pub(crate) use windows::{AnonPipe, pipe}; - - #[cfg(all(test, not(miri)))] - mod tests; + pub use windows::{AnonPipe, pipe}; } else { mod unsupported; - pub(crate) use unsupported::{AnonPipe, pipe}; + pub use unsupported::{AnonPipe, pipe}; } } diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs index 7c0e75208c43c..9168024730e67 100644 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -6,10 +6,10 @@ use crate::sys::fd::FileDesc; use crate::sys::pipe::anon_pipe; use crate::sys_common::{FromInner, IntoInner}; -pub(crate) type AnonPipe = FileDesc; +pub type AnonPipe = FileDesc; #[inline] -pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) } @@ -34,7 +34,7 @@ impl From for OwnedFd { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawFd for PipeReader { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FileDesc::from_raw_fd(raw_fd)) + unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] @@ -71,7 +71,7 @@ impl From for OwnedFd { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawFd for PipeWriter { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FileDesc::from_raw_fd(raw_fd)) + unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs index f3c826d2f8dfb..dd51e70315e96 100644 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -1,10 +1,10 @@ use crate::io; use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; -pub(crate) use crate::sys::pipe::AnonPipe; +pub use crate::sys::pipe::AnonPipe; #[inline] -pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { Err(io::Error::UNSUPPORTED_PLATFORM) } diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs index 0f746b1082baf..a48198f8a812b 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -1,18 +1,26 @@ -use crate::io; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; +use crate::sys::c; use crate::sys::handle::Handle; -use crate::sys::pipe::unnamed_anon_pipe; use crate::sys_common::{FromInner, IntoInner}; +use crate::{io, ptr}; -pub(crate) type AnonPipe = Handle; +pub type AnonPipe = Handle; -#[inline] -pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut read_pipe = c::INVALID_HANDLE_VALUE; + let mut write_pipe = c::INVALID_HANDLE_VALUE; + + let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + unsafe { Ok((Handle::from_raw_handle(read_pipe), Handle::from_raw_handle(write_pipe))) } + } } #[unstable(feature = "anonymous_pipe", issue = "127154")] @@ -31,7 +39,7 @@ impl AsRawHandle for PipeReader { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawHandle for PipeReader { unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - Self(Handle::from_raw_handle(raw_handle)) + unsafe { Self(Handle::from_raw_handle(raw_handle)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] @@ -70,7 +78,7 @@ impl AsRawHandle for PipeWriter { #[unstable(feature = "anonymous_pipe", issue = "127154")] impl FromRawHandle for PipeWriter { unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - Self(Handle::from_raw_handle(raw_handle)) + unsafe { Self(Handle::from_raw_handle(raw_handle)) } } } #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 99df503b82de2..2997e908fa1b2 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -28,6 +28,21 @@ extern "C" { pub fn lgamma_r(n: f64, s: &mut i32) -> f64; pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub fn acosf128(n: f128) -> f128; + pub fn asinf128(n: f128) -> f128; + pub fn atanf128(n: f128) -> f128; + pub fn atan2f128(a: f128, b: f128) -> f128; + pub fn cbrtf128(n: f128) -> f128; + pub fn coshf128(n: f128) -> f128; + pub fn expm1f128(n: f128) -> f128; + pub fn hypotf128(x: f128, y: f128) -> f128; + pub fn log1pf128(n: f128) -> f128; + pub fn sinhf128(n: f128) -> f128; + pub fn tanf128(n: f128) -> f128; + pub fn tanhf128(n: f128) -> f128; + pub fn tgammaf128(n: f128) -> f128; + pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { pub fn acosf(n: f32) -> f32; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 202997b749513..a86b3628f249a 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -7,7 +7,6 @@ mod pal; mod personality; -#[unstable(feature = "anonymous_pipe", issue = "127154")] pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; diff --git a/library/std/src/sys/pal/hermit/alloc.rs b/library/std/src/sys/pal/hermit/alloc.rs index 2cd0db909403b..f10d5f9227e63 100644 --- a/library/std/src/sys/pal/hermit/alloc.rs +++ b/library/std/src/sys/pal/hermit/alloc.rs @@ -1,31 +1,28 @@ use super::hermit_abi; use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - hermit_abi::malloc(layout.size(), layout.align()) - } - - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let addr = hermit_abi::malloc(layout.size(), layout.align()); - - if !addr.is_null() { - ptr::write_bytes(addr, 0x00, layout.size()); - } - - addr + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::malloc(size, align) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - hermit_abi::free(ptr, layout.size(), layout.align()) + let size = layout.size(); + let align = layout.align(); + unsafe { + hermit_abi::free(ptr, size, align); + } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - hermit_abi::realloc(ptr, layout.size(), layout.align(), new_size) + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::realloc(ptr, size, align, new_size) } } } diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index bdcf880484dfc..79fc13bd4a87f 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -111,7 +111,8 @@ impl FromInner for FileDesc { impl FromRawFd for FileDesc { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self { fd: FromRawFd::from_raw_fd(raw_fd) } + let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + Self { fd } } } diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index cbdb942ac58ef..aaf1a044d0613 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -484,7 +484,8 @@ impl IntoRawFd for File { impl FromRawFd for File { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) + let file_desc = unsafe { FileDesc::from_raw_fd(raw_fd) }; + Self(file_desc) } } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 55583b89d6714..ef406b9ec7f0d 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -13,7 +13,8 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -#![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] +#![allow(missing_docs, nonstandard_style)] use crate::os::raw::c_char; @@ -49,9 +50,7 @@ pub fn unsupported_err() -> crate::io::Error { } pub fn abort_internal() -> ! { - unsafe { - hermit_abi::abort(); - } + unsafe { hermit_abi::abort() } } pub fn hashmap_random_keys() -> (u64, u64) { @@ -80,7 +79,9 @@ pub extern "C" fn __rust_abort() { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { - args::init(argc, argv); + unsafe { + args::init(argc, argv); + } } // SAFETY: must be called only once during runtime cleanup. @@ -101,10 +102,12 @@ pub unsafe extern "C" fn runtime_entry( // initialize environment os::init_environment(env as *const *const i8); - let result = main(argc as isize, argv); + let result = unsafe { main(argc as isize, argv) }; - crate::sys::thread_local::destructors::run(); - hermit_abi::exit(result); + unsafe { + crate::sys::thread_local::destructors::run(); + } + unsafe { hermit_abi::exit(result) } } #[inline] diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 9631dac658c9c..f8ea80afa43f1 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -68,21 +68,21 @@ pub fn current_exe() -> io::Result { unsupported() } -static mut ENV: Option>> = None; +static ENV: Mutex>> = Mutex::new(None); pub fn init_environment(env: *const *const i8) { - unsafe { - ENV = Some(Mutex::new(HashMap::new())); + let mut guard = ENV.lock().unwrap(); + let map = guard.insert(HashMap::new()); - if env.is_null() { - return; - } + if env.is_null() { + return; + } - let mut guard = ENV.as_ref().unwrap().lock().unwrap(); + unsafe { let mut environ = env; while !(*environ).is_null() { if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { - guard.insert(key, value); + map.insert(key, value); } environ = environ.add(1); } @@ -154,30 +154,26 @@ impl Iterator for Env { /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { - unsafe { - let guard = ENV.as_ref().unwrap().lock().unwrap(); - let mut result = Vec::new(); + let guard = ENV.lock().unwrap(); + let env = guard.as_ref().unwrap(); - for (key, value) in guard.iter() { - result.push((key.clone(), value.clone())); - } + let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::>(); - return Env { iter: result.into_iter() }; - } + Env { iter: result.into_iter() } } pub fn getenv(k: &OsStr) -> Option { - unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() } + ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() } pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let (k, v) = (k.to_owned(), v.to_owned()); - ENV.as_ref().unwrap().lock().unwrap().insert(k, v); + ENV.lock().unwrap().as_mut().unwrap().insert(k, v); Ok(()) } pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - ENV.as_ref().unwrap().lock().unwrap().remove(k); + ENV.lock().unwrap().as_mut().unwrap().remove(k); Ok(()) } @@ -190,9 +186,7 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { - unsafe { - hermit_abi::exit(code); - } + unsafe { hermit_abi::exit(code) } } pub fn getpid() -> u32 { diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 95c13e53b470b..6321f92e3d9d0 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -25,18 +25,22 @@ impl Thread { core_id: isize, ) -> io::Result { let p = Box::into_raw(Box::new(p)); - let tid = hermit_abi::spawn2( - thread_start, - p.expose_provenance(), - hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), - stack, - core_id, - ); + let tid = unsafe { + hermit_abi::spawn2( + thread_start, + p.expose_provenance(), + hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), + stack, + core_id, + ) + }; return if tid == 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + unsafe { + drop(Box::from_raw(p)); + } Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) } else { Ok(Thread { tid: tid }) @@ -54,7 +58,9 @@ impl Thread { } pub unsafe fn new(stack: usize, p: Box) -> io::Result { - Thread::new_with_coreid(stack, p, -1 /* = no specific core */) + unsafe { + Thread::new_with_coreid(stack, p, -1 /* = no specific core */) + } } #[inline] diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index df0176244489a..9be018c8a5312 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -76,23 +76,5 @@ cfg_if::cfg_if! { } } -#[cfg(not(test))] -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { - pub use self::android::log2f32; - pub use self::android::log2f64; - } else { - #[inline] - pub fn log2f32(n: f32) -> f32 { - unsafe { crate::intrinsics::log2f32(n) } - } - - #[inline] - pub fn log2f64(n: f64) -> f64 { - unsafe { crate::intrinsics::log2f64(n) } - } - } -} - #[cfg(not(target_os = "uefi"))] pub type RawOsError = i32; diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index 298095257396a..5069ab82ccc90 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -8,6 +8,7 @@ use crate::cell::UnsafeCell; use crate::convert::TryInto; use crate::mem::{self, ManuallyDrop}; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; +use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; use crate::slice::SliceIndex; use crate::{cmp, intrinsics, slice}; @@ -751,6 +752,9 @@ where #[unstable(feature = "sgx_platform", issue = "56975")] impl, U> CoerceUnsized> for UserRef {} +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UserRef {} + #[unstable(feature = "sgx_platform", issue = "56975")] impl Index for UserRef<[T]> where diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index df65d1cc8fc48..cecd53c352c5a 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -1,4 +1,5 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? + use super::abi::usercalls; use super::unsupported; use crate::ffi::CStr; diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index a97d69f997bf9..495ff2dc930ed 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -175,10 +175,6 @@ pub(crate) mod instant_internal { #[cfg(target_arch = "x86_64")] fn timestamp_rdtsc() -> Option { - if !crate::arch::x86_64::has_cpuid() { - return None; - } - static FREQUENCY: crate::sync::OnceLock = crate::sync::OnceLock::new(); // Get Frequency in Mhz @@ -200,10 +196,6 @@ pub(crate) mod instant_internal { #[cfg(target_arch = "x86")] fn timestamp_rdtsc() -> Option { - if !crate::arch::x86::has_cpuid() { - return None; - } - static FREQUENCY: crate::sync::OnceLock = crate::sync::OnceLock::new(); let freq = FREQUENCY diff --git a/library/std/src/sys/pal/unix/android.rs b/library/std/src/sys/pal/unix/android.rs deleted file mode 100644 index 0f704994f550a..0000000000000 --- a/library/std/src/sys/pal/unix/android.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! Android ABI-compatibility module -//! -//! The ABI of Android has changed quite a bit over time, and std attempts to be -//! both forwards and backwards compatible as much as possible. We want to -//! always work with the most recent version of Android, but we also want to -//! work with older versions of Android for whenever projects need to. -//! -//! Our current minimum supported Android version is `android-9`, e.g., Android -//! with API level 9. We then in theory want to work on that and all future -//! versions of Android! -//! -//! Some of the detection here is done at runtime via `dlopen` and -//! introspection. Other times no detection is performed at all and we just -//! provide a fallback implementation as some versions of Android we support -//! don't have the function. -//! -//! You'll find more details below about why each compatibility shim is needed. - -#![cfg(target_os = "android")] - -use libc::{c_int, sighandler_t}; - -use super::weak::weak; - -// The `log2` and `log2f` functions apparently appeared in android-18, or at -// least you can see they're not present in the android-17 header [1] and they -// are present in android-18 [2]. -// -// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-17/arch-arm/usr/include/math.h -// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-18/arch-arm/usr/include/math.h -// -// Note that these shims are likely less precise than directly calling `log2`, -// but hopefully that should be enough for now... -// -// Note that mathematically, for any arbitrary `y`: -// -// log_2(x) = log_y(x) / log_y(2) -// = log_y(x) / (1 / log_2(y)) -// = log_y(x) * log_2(y) -// -// Hence because `ln` (log_e) is available on all Android we just choose `y = e` -// and get: -// -// log_2(x) = ln(x) * log_2(e) - -#[cfg(not(test))] -pub fn log2f32(f: f32) -> f32 { - f.ln() * crate::f32::consts::LOG2_E -} - -#[cfg(not(test))] -pub fn log2f64(f: f64) -> f64 { - f.ln() * crate::f64::consts::LOG2_E -} - -// Back in the day [1] the `signal` function was just an inline wrapper -// around `bsd_signal`, but starting in API level android-20 the `signal` -// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was -// removed [3]. -// -// Basically this means that if we want to be binary compatible with multiple -// Android releases (oldest being 9 and newest being 21) then we need to check -// for both symbols and not actually link against either. -// -// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-18/arch-arm/usr/include/signal.h -// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental -// /platforms/android-20/arch-arm -// /usr/include/signal.h -// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms -// /android-21/arch-arm/usr/include/signal.h -pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { - weak!(fn signal(c_int, sighandler_t) -> sighandler_t); - weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t); - - let f = signal.get().or_else(|| bsd_signal.get()); - let f = f.expect("neither `signal` nor `bsd_signal` symbols found"); - f(signum, handler) -} diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index 652b2e1feb7ad..a671383cb7957 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -60,6 +60,7 @@ use crate::net::TcpStream; use crate::os::unix::fs::FileTypeExt; use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; +use crate::pipe::{PipeReader, PipeWriter}; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; @@ -405,6 +406,30 @@ impl CopyWrite for &UnixStream { } } +impl CopyRead for PipeReader { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + +impl CopyRead for &PipeReader { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + +impl CopyWrite for PipeWriter { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + +impl CopyWrite for &PipeWriter { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + impl CopyWrite for ChildStdin { fn properties(&self) -> CopyParams { CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3fc51ff59a936..b62129f4cdd26 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -8,7 +8,6 @@ use crate::io::ErrorKind; pub mod weak; pub mod alloc; -pub mod android; pub mod args; pub mod env; pub mod fd; @@ -237,12 +236,8 @@ pub unsafe fn cleanup() { } #[allow(unused_imports)] -#[cfg(not(target_os = "android"))] pub use libc::signal; -#[cfg(target_os = "android")] -pub use crate::sys::android::signal; - #[inline] pub(crate) fn is_interrupted(errno: i32) -> bool { errno == libc::EINTR diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 8762af614f17e..f0ebc767badad 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -47,6 +47,8 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + #[allow(dead_code)] + // FIXME: This function seems legitimately unused. pub fn try_clone(&self) -> io::Result { self.0.duplicate().map(Self) } @@ -85,6 +87,8 @@ impl AnonPipe { self.0.is_write_vectored() } + #[allow(dead_code)] + // FIXME: This function seems legitimately unused. pub fn as_file_desc(&self) -> &FileDesc { &self.0 } diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs index 6a9d8fab1d412..0477b3d9a70da 100644 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use libc::{self, c_char, c_int, RTP_ID}; use crate::io::{self, ErrorKind}; diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 83034761f3d16..7d29d9a517210 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -3,7 +3,12 @@ use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; -#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] +#[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + target_os = "vxworks" +))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; use crate::time::Duration; @@ -212,17 +217,38 @@ impl Thread { } } + #[cfg(target_os = "vxworks")] + pub fn set_name(name: &CStr) { + // FIXME(libc): adding real STATUS, ERROR type eventually. + weak! { + fn taskNameSet( + libc::TASK_ID, *mut libc::c_char + ) -> libc::c_int + } + + // We can't assume taskNameSet is necessarily available. + // VX_TASK_NAME_LEN can be found set to 31, + // however older versions can be set to only 10. + // FIXME(vxworks): if the minimum supported VxWorks is >= 7, the maximum length can be changed to 31. + if let Some(f) = taskNameSet.get() { + const VX_TASK_NAME_LEN: usize = 10; + + let name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); + let status = unsafe { f(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); + } + } + #[cfg(any( target_env = "newlib", target_os = "l4re", target_os = "emscripten", target_os = "redox", - target_os = "vxworks", target_os = "hurd", target_os = "aix", ))] pub fn set_name(_name: &CStr) { - // Newlib, Emscripten, and VxWorks have no way to set a thread name. + // Newlib and Emscripten have no way to set a thread name. } #[cfg(not(target_os = "espidf"))] @@ -291,6 +317,7 @@ impl Drop for Thread { target_os = "nto", target_os = "solaris", target_os = "illumos", + target_os = "vxworks", target_vendor = "apple", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { @@ -455,8 +482,18 @@ pub fn available_parallelism() -> io::Result> { Ok(NonZero::new_unchecked(sinfo.cpu_count as usize)) } + } else if #[cfg(target_os = "vxworks")] { + // Note: there is also `vxCpuConfiguredGet`, closer to _SC_NPROCESSORS_CONF + // expectations than the actual cores availability. + extern "C" { + fn vxCpuEnabledGet() -> libc::cpuset_t; + } + + // always fetches a valid bitmask + let set = unsafe { vxCpuEnabledGet() }; + Ok(NonZero::new_unchecked(set.count_ones() as usize)) } else { - // FIXME: implement on vxWorks, Redox, l4re + // FIXME: implement on Redox, l4re Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) } } diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index b1b6aae757c51..35762f5a53b5b 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -168,14 +168,7 @@ pub(crate) macro syscall { if let Some(fun) = $name.get() { fun($($arg_name),*) } else { - // This looks like a hack, but concat_idents only accepts idents - // (not paths). - use libc::*; - - syscall( - concat_idents!(SYS_, $name), - $($arg_name),* - ) as $ret + libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret } } ) @@ -185,14 +178,7 @@ pub(crate) macro syscall { pub(crate) macro raw_syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name:$t),*) -> $ret { - // This looks like a hack, but concat_idents only accepts idents - // (not paths). - use libc::*; - - syscall( - concat_idents!(SYS_, $name), - $($arg_name),* - ) as $ret + libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret } ) } diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index e27fdb72256f4..ce995f5ed5af7 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -21,6 +21,7 @@ pub mod netc { //! //! Some Windows API types are not quite what's expected by our cross-platform //! net code. E.g. naming differences or different pointer types. + use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 9eb19fcf2d7a2..7d1b5aca1d5fe 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -36,23 +36,6 @@ pub struct Pipes { pub theirs: AnonPipe, } -/// Create true unnamed anonymous pipe. -pub fn unnamed_anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut read_pipe = c::INVALID_HANDLE_VALUE; - let mut write_pipe = c::INVALID_HANDLE_VALUE; - - let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; - - if ret == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(( - AnonPipe::from_inner(unsafe { Handle::from_raw_handle(read_pipe) }), - AnonPipe::from_inner(unsafe { Handle::from_raw_handle(write_pipe) }), - )) - } -} - /// Although this looks similar to `anon_pipe` in the Unix module it's actually /// subtly different. Here we'll return two pipes in the `Pipes` return value, /// but one is intended for "us" where as the other is intended for "someone diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index c285e1530a521..06eae5a07b068 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -564,7 +564,7 @@ impl Stdio { Ok(io) => unsafe { let io = Handle::from_raw_handle(io); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw_handle(); + let _ = io.into_raw_handle(); // Don't close the handle ret }, // If no stdio handle is available, then propagate the null value. diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index dc63a2219c315..575f2250eb91c 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -94,7 +94,7 @@ fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> i unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); - handle.into_raw_handle(); // Don't close the handle + let _ = handle.into_raw_handle(); // Don't close the handle return ret; } } @@ -243,7 +243,7 @@ impl io::Read for Stdin { unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.read(buf); - handle.into_raw_handle(); // Don't close the handle + let _ = handle.into_raw_handle(); // Don't close the handle return ret; } } diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index e683777803f02..2c8a054282b01 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -6,7 +6,7 @@ use crate::sync::once::ExclusiveState; use crate::sys::futex::{futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. -// This means we only need one atomic value with 5 states: +// This means we only need one atomic value with 4 states: /// No initialization has run yet, and no thread is currently using the Once. const INCOMPLETE: u32 = 0; @@ -17,16 +17,20 @@ const POISONED: u32 = 1; /// Some thread is currently attempting to run initialization. It may succeed, /// so all future threads need to wait for it to finish. const RUNNING: u32 = 2; -/// Some thread is currently attempting to run initialization and there are threads -/// waiting for it to finish. -const QUEUED: u32 = 3; /// Initialization has completed and all future calls should finish immediately. -const COMPLETE: u32 = 4; +const COMPLETE: u32 = 3; -// Threads wait by setting the state to QUEUED and calling `futex_wait` on the state +// An additional bit indicates whether there are waiting threads: + +/// May only be set if the state is not COMPLETE. +const QUEUED: u32 = 4; + +// Threads wait by setting the QUEUED bit and calling `futex_wait` on the state // variable. When the running thread finishes, it will wake all waiting threads using // `futex_wake_all`. +const STATE_MASK: u32 = 0b11; + pub struct OnceState { poisoned: bool, set_state_to: Cell, @@ -45,7 +49,7 @@ impl OnceState { } struct CompletionGuard<'a> { - state: &'a AtomicU32, + state_and_queued: &'a AtomicU32, set_state_on_drop_to: u32, } @@ -54,32 +58,32 @@ impl<'a> Drop for CompletionGuard<'a> { // Use release ordering to propagate changes to all threads checking // up on the Once. `futex_wake_all` does its own synchronization, hence // we do not need `AcqRel`. - if self.state.swap(self.set_state_on_drop_to, Release) == QUEUED { - futex_wake_all(self.state); + if self.state_and_queued.swap(self.set_state_on_drop_to, Release) & QUEUED != 0 { + futex_wake_all(self.state_and_queued); } } } pub struct Once { - state: AtomicU32, + state_and_queued: AtomicU32, } impl Once { #[inline] pub const fn new() -> Once { - Once { state: AtomicU32::new(INCOMPLETE) } + Once { state_and_queued: AtomicU32::new(INCOMPLETE) } } #[inline] pub fn is_completed(&self) -> bool { // Use acquire ordering to make all initialization changes visible to the // current thread. - self.state.load(Acquire) == COMPLETE + self.state_and_queued.load(Acquire) == COMPLETE } #[inline] pub(crate) fn state(&mut self) -> ExclusiveState { - match *self.state.get_mut() { + match *self.state_and_queued.get_mut() { INCOMPLETE => ExclusiveState::Incomplete, POISONED => ExclusiveState::Poisoned, COMPLETE => ExclusiveState::Complete, @@ -87,31 +91,73 @@ impl Once { } } - // This uses FnMut to match the API of the generic implementation. As this - // implementation is quite light-weight, it is generic over the closure and - // so avoids the cost of dynamic dispatch. #[cold] #[track_caller] - pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { - let mut state = self.state.load(Acquire); + pub fn wait(&self, ignore_poisoning: bool) { + let mut state_and_queued = self.state_and_queued.load(Acquire); loop { + let state = state_and_queued & STATE_MASK; + let queued = state_and_queued & QUEUED != 0; match state { + COMPLETE => return, + POISONED if !ignore_poisoning => { + // Panic to propagate the poison. + panic!("Once instance has previously been poisoned"); + } + _ => { + // Set the QUEUED bit if it has not already been set. + if !queued { + state_and_queued += QUEUED; + if let Err(new) = self.state_and_queued.compare_exchange_weak( + state, + state_and_queued, + Relaxed, + Acquire, + ) { + state_and_queued = new; + continue; + } + } + + futex_wait(&self.state_and_queued, state_and_queued, None); + state_and_queued = self.state_and_queued.load(Acquire); + } + } + } + } + + #[cold] + #[track_caller] + pub fn call(&self, ignore_poisoning: bool, f: &mut dyn FnMut(&public::OnceState)) { + let mut state_and_queued = self.state_and_queued.load(Acquire); + loop { + let state = state_and_queued & STATE_MASK; + let queued = state_and_queued & QUEUED != 0; + match state { + COMPLETE => return, POISONED if !ignore_poisoning => { // Panic to propagate the poison. panic!("Once instance has previously been poisoned"); } INCOMPLETE | POISONED => { // Try to register the current thread as the one running. - if let Err(new) = - self.state.compare_exchange_weak(state, RUNNING, Acquire, Acquire) - { - state = new; + let next = RUNNING + if queued { QUEUED } else { 0 }; + if let Err(new) = self.state_and_queued.compare_exchange_weak( + state_and_queued, + next, + Acquire, + Acquire, + ) { + state_and_queued = new; continue; } + // `waiter_queue` will manage other waiting threads, and // wake them up on drop. - let mut waiter_queue = - CompletionGuard { state: &self.state, set_state_on_drop_to: POISONED }; + let mut waiter_queue = CompletionGuard { + state_and_queued: &self.state_and_queued, + set_state_on_drop_to: POISONED, + }; // Run the function, letting it know if we're poisoned or not. let f_state = public::OnceState { inner: OnceState { @@ -123,21 +169,27 @@ impl Once { waiter_queue.set_state_on_drop_to = f_state.inner.set_state_to.get(); return; } - RUNNING | QUEUED => { - // Set the state to QUEUED if it is not already. - if state == RUNNING - && let Err(new) = - self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire) - { - state = new; - continue; + _ => { + // All other values must be RUNNING. + assert!(state == RUNNING); + + // Set the QUEUED bit if it is not already set. + if !queued { + state_and_queued += QUEUED; + if let Err(new) = self.state_and_queued.compare_exchange_weak( + state, + state_and_queued, + Relaxed, + Acquire, + ) { + state_and_queued = new; + continue; + } } - futex_wait(&self.state, QUEUED, None); - state = self.state.load(Acquire); + futex_wait(&self.state_and_queued, state_and_queued, None); + state_and_queued = self.state_and_queued.load(Acquire); } - COMPLETE => return, - _ => unreachable!("state is never set to invalid values"), } } } diff --git a/library/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs index 11fde1888ba7c..12c1d9f5a6c98 100644 --- a/library/std/src/sys/sync/once/no_threads.rs +++ b/library/std/src/sys/sync/once/no_threads.rs @@ -55,6 +55,12 @@ impl Once { } } + #[cold] + #[track_caller] + pub fn wait(&self, _ignore_poisoning: bool) { + panic!("not implementable on this target"); + } + #[cold] #[track_caller] pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index b04d252f8b91f..86f72c82008bc 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -56,20 +56,21 @@ // allowed, so no need for `SeqCst`. use crate::cell::Cell; -use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; +use crate::sync::atomic::{AtomicBool, AtomicPtr}; use crate::sync::once::ExclusiveState; use crate::thread::{self, Thread}; use crate::{fmt, ptr, sync as public}; -type Masked = (); +type StateAndQueue = *mut (); pub struct Once { - state_and_queue: AtomicPtr, + state_and_queue: AtomicPtr<()>, } pub struct OnceState { poisoned: bool, - set_state_on_drop_to: Cell<*mut Masked>, + set_state_on_drop_to: Cell, } // Four states that a Once can be in, encoded into the lower bits of @@ -81,7 +82,8 @@ const COMPLETE: usize = 0x3; // Mask to learn about the state. All other bits are the queue of waiters if // this is in the RUNNING state. -const STATE_MASK: usize = 0x3; +const STATE_MASK: usize = 0b11; +const QUEUE_MASK: usize = !STATE_MASK; // Representation of a node in the linked list of waiters, used while in the // RUNNING state. @@ -93,15 +95,23 @@ const STATE_MASK: usize = 0x3; struct Waiter { thread: Cell>, signaled: AtomicBool, - next: *const Waiter, + next: Cell<*const Waiter>, } // Head of a linked list of waiters. // Every node is a struct on the stack of a waiting thread. // Will wake up the waiters when it gets dropped, i.e. also on panic. struct WaiterQueue<'a> { - state_and_queue: &'a AtomicPtr, - set_state_on_drop_to: *mut Masked, + state_and_queue: &'a AtomicPtr<()>, + set_state_on_drop_to: StateAndQueue, +} + +fn to_queue(current: StateAndQueue) -> *const Waiter { + current.mask(QUEUE_MASK).cast() +} + +fn to_state(current: StateAndQueue) -> usize { + current.addr() & STATE_MASK } impl Once { @@ -117,7 +127,7 @@ impl Once { // operations visible to us, and, this being a fast path, weaker // ordering helps with performance. This `Acquire` synchronizes with // `Release` operations on the slow path. - self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE + self.state_and_queue.load(Acquire).addr() == COMPLETE } #[inline] @@ -130,6 +140,25 @@ impl Once { } } + #[cold] + #[track_caller] + pub fn wait(&self, ignore_poisoning: bool) { + let mut current = self.state_and_queue.load(Acquire); + loop { + let state = to_state(current); + match state { + COMPLETE => return, + POISONED if !ignore_poisoning => { + // Panic to propagate the poison. + panic!("Once instance has previously been poisoned"); + } + _ => { + current = wait(&self.state_and_queue, current, !ignore_poisoning); + } + } + } + } + // This is a non-generic function to reduce the monomorphization cost of // using `call_once` (this isn't exactly a trivial or small implementation). // @@ -144,9 +173,10 @@ impl Once { #[cold] #[track_caller] pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) { - let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire); + let mut current = self.state_and_queue.load(Acquire); loop { - match state_and_queue.addr() { + let state = to_state(current); + match state { COMPLETE => break, POISONED if !ignore_poisoning => { // Panic to propagate the poison. @@ -154,16 +184,16 @@ impl Once { } POISONED | INCOMPLETE => { // Try to register this thread as the one RUNNING. - let exchange_result = self.state_and_queue.compare_exchange( - state_and_queue, - ptr::without_provenance_mut(RUNNING), - Ordering::Acquire, - Ordering::Acquire, - ); - if let Err(old) = exchange_result { - state_and_queue = old; + if let Err(new) = self.state_and_queue.compare_exchange_weak( + current, + current.mask(QUEUE_MASK).wrapping_byte_add(RUNNING), + Acquire, + Acquire, + ) { + current = new; continue; } + // `waiter_queue` will manage other waiting threads, and // wake them up on drop. let mut waiter_queue = WaiterQueue { @@ -174,54 +204,57 @@ impl Once { // poisoned or not. let init_state = public::OnceState { inner: OnceState { - poisoned: state_and_queue.addr() == POISONED, + poisoned: state == POISONED, set_state_on_drop_to: Cell::new(ptr::without_provenance_mut(COMPLETE)), }, }; init(&init_state); waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get(); - break; + return; } _ => { // All other values must be RUNNING with possibly a // pointer to the waiter queue in the more significant bits. - assert!(state_and_queue.addr() & STATE_MASK == RUNNING); - wait(&self.state_and_queue, state_and_queue); - state_and_queue = self.state_and_queue.load(Ordering::Acquire); + assert!(state == RUNNING); + current = wait(&self.state_and_queue, current, true); } } } } } -fn wait(state_and_queue: &AtomicPtr, mut current_state: *mut Masked) { - // Note: the following code was carefully written to avoid creating a - // mutable reference to `node` that gets aliased. +fn wait( + state_and_queue: &AtomicPtr<()>, + mut current: StateAndQueue, + return_on_poisoned: bool, +) -> StateAndQueue { + let node = &Waiter { + thread: Cell::new(Some(thread::current())), + signaled: AtomicBool::new(false), + next: Cell::new(ptr::null()), + }; + loop { - // Don't queue this thread if the status is no longer running, - // otherwise we will not be woken up. - if current_state.addr() & STATE_MASK != RUNNING { - return; + let state = to_state(current); + let queue = to_queue(current); + + // If initialization has finished, return. + if state == COMPLETE || (return_on_poisoned && state == POISONED) { + return current; } - // Create the node for our current thread. - let node = Waiter { - thread: Cell::new(Some(thread::current())), - signaled: AtomicBool::new(false), - next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter, - }; - let me = core::ptr::addr_of!(node) as *const Masked as *mut Masked; + // Update the node for our current thread. + node.next.set(queue); // Try to slide in the node at the head of the linked list, making sure // that another thread didn't just replace the head of the linked list. - let exchange_result = state_and_queue.compare_exchange( - current_state, - me.with_addr(me.addr() | RUNNING), - Ordering::Release, - Ordering::Relaxed, - ); - if let Err(old) = exchange_result { - current_state = old; + if let Err(new) = state_and_queue.compare_exchange_weak( + current, + ptr::from_ref(node).wrapping_byte_add(state) as StateAndQueue, + Release, + Acquire, + ) { + current = new; continue; } @@ -230,14 +263,15 @@ fn wait(state_and_queue: &AtomicPtr, mut current_state: *mut Masked) { // would drop our `Waiter` node and leave a hole in the linked list // (and a dangling reference). Guard against spurious wakeups by // reparking ourselves until we are signaled. - while !node.signaled.load(Ordering::Acquire) { + while !node.signaled.load(Acquire) { // If the managing thread happens to signal and unpark us before we // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got // an `unpark` just before on an unparked thread it does not park. thread::park(); } - break; + + return state_and_queue.load(Acquire); } } @@ -251,11 +285,10 @@ impl fmt::Debug for Once { impl Drop for WaiterQueue<'_> { fn drop(&mut self) { // Swap out our state with however we finished. - let state_and_queue = - self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel); + let current = self.state_and_queue.swap(self.set_state_on_drop_to, AcqRel); // We should only ever see an old state which was RUNNING. - assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING); + assert_eq!(current.addr() & STATE_MASK, RUNNING); // Walk the entire linked list of waiters and wake them up (in lifo // order, last to register is first to wake up). @@ -264,16 +297,13 @@ impl Drop for WaiterQueue<'_> { // free `node` if there happens to be has a spurious wakeup. // So we have to take out the `thread` field and copy the pointer to // `next` first. - let mut queue = - state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter; + let mut queue = to_queue(current); while !queue.is_null() { - let next = (*queue).next; + let next = (*queue).next.get(); let thread = (*queue).thread.take().unwrap(); - (*queue).signaled.store(true, Ordering::Release); - // ^- FIXME (maybe): This is another case of issue #55005 - // `store()` has a potentially dangling ref to `signaled`. - queue = next; + (*queue).signaled.store(true, Release); thread.unpark(); + queue = next; } } } diff --git a/library/stdarch b/library/stdarch index df3618d9f3516..47b929ddc521a 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit df3618d9f35165f4bc548114e511c49c29e1fd9b +Subproject commit 47b929ddc521a78b0f699ba8d5c274d28593448a diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 45a1c334a44dd..b3de71f29f394 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -2,7 +2,6 @@ #![unstable(feature = "panic_unwind", issue = "32837")] #![feature(link_cfg)] #![feature(staged_api)] -#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(strict_provenance)] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] diff --git a/rustfmt.toml b/rustfmt.toml index 5aafb83b2bcbd..60cd03484007d 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -24,8 +24,6 @@ ignore = [ "/tests/rustdoc-ui/", # Some have syntax errors, some are whitespace-sensitive. "/tests/ui/", # Some have syntax errors, some are whitespace-sensitive. "/tests/ui-fulldeps/", # Some are whitespace-sensitive (e.g. `// ~ERROR` comments). - # #[cfg(bootstrap)] so that t-release sees this when they search for it - "/tests/rustdoc-json/impl-trait-precise-capturing.rs", # Do not format submodules. # FIXME: sync submodule list with tidy/bootstrap/etc diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index de0924c0f4236..60453764d82d3 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -65,7 +65,7 @@ dependencies = [ "termcolor", "toml", "walkdir", - "windows", + "windows 0.52.0", "xz2", ] @@ -378,12 +378,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - [[package]] name = "opener" version = "0.5.2" @@ -549,16 +543,15 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.5" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2" +checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab" dependencies = [ - "cfg-if", "core-foundation-sys", "libc", + "memchr", "ntapi", - "once_cell", - "windows", + "windows 0.57.0", ] [[package]] @@ -655,7 +648,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets", +] + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", "windows-targets", ] @@ -668,6 +671,49 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -679,13 +725,14 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -694,45 +741,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xattr" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index f723407c3ce31..84262c115b120 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -63,7 +63,7 @@ walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature -sysinfo = { version = "0.30", default-features = false, optional = true } +sysinfo = { version = "0.31.2", default-features = false, optional = true, features = ["system"] } [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 6a386732029bd..dc8b5487a61e8 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -26,10 +26,7 @@ fn main() { // Display PID of process holding the lock // PID will be stored in a lock file let lock_path = config.out.join("lock"); - let pid = match fs::read_to_string(&lock_path) { - Ok(contents) => contents, - Err(_) => String::new(), - }; + let pid = fs::read_to_string(&lock_path).unwrap_or_default(); build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new() .write(true) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 8b71300cf85e7..7f7faf077d047 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -31,10 +31,6 @@ pub struct Std { } impl Std { - pub fn new(target: TargetSelection) -> Self { - Self::new_with_build_kind(target, None) - } - pub fn new_with_build_kind(target: TargetSelection, kind: Option) -> Self { Self { target, crates: vec![], override_build_kind: kind } } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 268e89c7f6011..c09180e542ff6 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -26,7 +26,8 @@ use crate::core::builder::{ use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; use crate::utils::exec::command; use crate::utils::helpers::{ - exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, + self, exe, get_clang_cl_resource_dir, get_closest_merge_base_commit, is_debug_info, is_dylib, + symlink_dir, t, up_to_date, }; use crate::{CLang, Compiler, DependencyType, GitRepo, Mode, LLVM_TOOLS}; @@ -114,21 +115,43 @@ impl Step for Std { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // When downloading stage1, the standard library has already been copied to the sysroot, so - // there's no need to rebuild it. - let builder = run.builder; - run.crate_or_deps("sysroot") - .path("library") - .lazy_default_condition(Box::new(|| !builder.download_rustc())) + run.crate_or_deps("sysroot").path("library") } fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); + let builder = run.builder; + + // Force compilation of the standard library from source if the `library` is modified. This allows + // library team to compile the standard library without needing to compile the compiler with + // the `rust.download-rustc=true` option. + let force_recompile = + if builder.rust_info().is_managed_git_subrepository() && builder.download_rustc() { + let closest_merge_commit = get_closest_merge_base_commit( + Some(&builder.src), + &builder.config.git_config(), + &builder.config.stage0_metadata.config.git_merge_commit_email, + &[], + ) + .unwrap(); + + // Check if `library` has changes (returns false otherwise) + !t!(helpers::git(Some(&builder.src)) + .args(["diff-index", "--quiet", &closest_merge_commit]) + .arg("--") + .arg(builder.src.join("library")) + .as_command_mut() + .status()) + .success() + } else { + false + }; + run.builder.ensure(Std { compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, crates, - force_recompile: false, + force_recompile, extra_rust_args: &[], is_for_mir_opt_tests: false, }); diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 967ddbc0d3483..43306eab1b144 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -918,7 +918,6 @@ impl Step for Src { // translation code in `imported_source_files` in `src/librustc_metadata/rmeta/decoder.rs` let dst_src = tarball.image_dir().join("lib/rustlib/src/rust"); - let src_files = ["Cargo.lock"]; // This is the reduced set of paths which will become the rust-src component // (essentially libstd and all of its path dependencies). copy_src_dirs( @@ -937,9 +936,6 @@ impl Step for Src { ], &dst_src, ); - for file in src_files.iter() { - builder.copy_link(&builder.src.join(file), &dst_src.join(file)); - } tarball.generate() } @@ -1034,6 +1030,8 @@ impl Step for PlainSourceTarball { .arg("--sync") .arg(builder.src.join("./compiler/rustc_codegen_gcc/Cargo.toml")) .arg("--sync") + .arg(builder.src.join("./library/Cargo.toml")) + .arg("--sync") .arg(builder.src.join("./src/bootstrap/Cargo.toml")) .arg("--sync") .arg(builder.src.join("./src/tools/opt-dist/Cargo.toml")) @@ -2122,8 +2120,13 @@ impl Step for LlvmTools { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let default = should_build_extended_tool(run.builder, "llvm-tools"); - // FIXME: allow using the names of the tools themselves? - run.alias("llvm-tools").default_condition(default) + + let mut run = run.alias("llvm-tools"); + for tool in LLVM_TOOLS { + run = run.alias(tool); + } + + run.default_condition(default) } fn make_run(run: RunConfig<'_>) { @@ -2131,6 +2134,32 @@ impl Step for LlvmTools { } fn run(self, builder: &Builder<'_>) -> Option { + fn tools_to_install(paths: &[PathBuf]) -> Vec<&'static str> { + let mut tools = vec![]; + + for path in paths { + let path = path.to_str().unwrap(); + + // Include all tools if path is 'llvm-tools'. + if path == "llvm-tools" { + return LLVM_TOOLS.to_owned(); + } + + for tool in LLVM_TOOLS { + if path == *tool { + tools.push(*tool); + } + } + } + + // If no specific tool is requested, include all tools. + if tools.is_empty() { + tools = LLVM_TOOLS.to_owned(); + } + + tools + } + let target = self.target; /* run only if llvm-config isn't used */ @@ -2151,7 +2180,7 @@ impl Step for LlvmTools { // Prepare the image directory let src_bindir = builder.llvm_out(target).join("bin"); let dst_bindir = format!("lib/rustlib/{}/bin", target.triple); - for tool in LLVM_TOOLS { + for tool in tools_to_install(&builder.paths) { let exe = src_bindir.join(exe(tool, target)); tarball.add_file(&exe, &dst_bindir, 0o755); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 1541396bfdd98..2cd5db706c26a 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -599,6 +599,16 @@ impl Step for Std { fn run(self, builder: &Builder<'_>) { let stage = self.stage; let target = self.target; + let crates = if self.crates.is_empty() { + builder + .in_tree_crates("sysroot", Some(target)) + .iter() + .map(|c| c.name.to_string()) + .collect() + } else { + self.crates + }; + let out = match self.format { DocumentationFormat::Html => builder.doc_out(target), DocumentationFormat::Json => builder.json_doc_out(target), @@ -627,7 +637,7 @@ impl Step for Std { extra_args.push("--disable-minification"); } - doc_std(builder, self.format, stage, target, &out, &extra_args, &self.crates); + doc_std(builder, self.format, stage, target, &out, &extra_args, &crates); // Don't open if the format is json if let DocumentationFormat::Json = self.format { @@ -639,7 +649,7 @@ impl Step for Std { let index = out.join("std").join("index.html"); builder.open_in_browser(index); } else { - for requested_crate in &*self.crates { + for requested_crate in crates { if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) { let index = out.join(requested_crate).join("index.html"); builder.open_in_browser(index); diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 81770036d71c4..33768465225fe 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -47,6 +47,7 @@ impl Step for Vendor { "src/tools/rust-analyzer/Cargo.toml", "compiler/rustc_codegen_cranelift/Cargo.toml", "compiler/rustc_codegen_gcc/Cargo.toml", + "library/Cargo.toml", "src/bootstrap/Cargo.toml", "src/tools/rustbook/Cargo.toml", ] { diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1343e257efe08..14beef20bad6e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -512,6 +512,11 @@ impl TargetSelection { pub fn is_windows(&self) -> bool { self.contains("windows") } + + /// Path to the file defining the custom target, if any. + pub fn filepath(&self) -> Option<&Path> { + self.file.as_ref().map(Path::new) + } } impl fmt::Display for TargetSelection { @@ -1840,6 +1845,23 @@ impl Config { config.llvm_from_ci = config.parse_download_ci_llvm(download_ci_llvm, asserts); if config.llvm_from_ci { + let warn = |option: &str| { + println!( + "WARNING: `{option}` will only be used on `compiler/rustc_llvm` build, not for the LLVM build." + ); + println!( + "HELP: To use `{option}` for LLVM builds, set `download-ci-llvm` option to false." + ); + }; + + if static_libstdcpp.is_some() { + warn("static-libstdcpp"); + } + + if link_shared.is_some() { + warn("link-shared"); + } + // None of the LLVM options, except assertions, are supported // when using downloaded LLVM. We could just ignore these but // that's potentially confusing, so force them to not be @@ -1849,9 +1871,6 @@ impl Config { check_ci_llvm!(optimize_toml); check_ci_llvm!(thin_lto); check_ci_llvm!(release_debuginfo); - // CI-built LLVM can be either dynamic or static. We won't know until we download it. - check_ci_llvm!(link_shared); - check_ci_llvm!(static_libstdcpp); check_ci_llvm!(targets); check_ci_llvm!(experimental_targets); check_ci_llvm!(clang_cl); diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs index 9b4c85e6d34a4..1016607fc8318 100644 --- a/src/bootstrap/src/core/metadata.rs +++ b/src/bootstrap/src/core/metadata.rs @@ -86,6 +86,9 @@ fn workspace_members(build: &Build) -> Vec { packages }; - // Collects `metadata.packages` from all workspaces. - collect_metadata("Cargo.toml") + // Collects `metadata.packages` from the root and library workspaces. + let mut packages = vec![]; + packages.extend(collect_metadata("Cargo.toml")); + packages.extend(collect_metadata("library/Cargo.toml")); + packages } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 45f4090ef2285..c42d4c56c3816 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -260,7 +260,9 @@ than building it. if !has_target { // This might also be a custom target, so check the target file that could have been specified by the user. - if let Some(custom_target_path) = env::var_os("RUST_TARGET_PATH") { + if target.filepath().is_some_and(|p| p.exists()) { + has_target = true; + } else if let Some(custom_target_path) = env::var_os("RUST_TARGET_PATH") { let mut target_filename = OsString::from(&target_str); // Target filename ends with `.json`. target_filename.push(".json"); @@ -275,8 +277,12 @@ than building it. if !has_target { panic!( - "No such target exists in the target list, - specify a correct location of the JSON specification file for custom targets!" + "No such target exists in the target list,\n\ + make sure to correctly specify the location \ + of the JSON specification file \ + for custom targets!\n\ + Use BOOTSTRAP_SKIP_TARGET_SANITY=1 to \ + bypass this check." ); } } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 16959ce7e8249..65e75f114bbeb 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -536,8 +536,7 @@ pub fn get_closest_merge_base_commit( let merge_base = get_git_merge_base(config, source_dir).unwrap_or_else(|_| "HEAD".into()); - git.arg(Path::new("rev-list")); - git.args([&format!("--author={author}"), "-n1", "--first-parent", &merge_base]); + git.args(["rev-list", &format!("--author={author}"), "-n1", "--first-parent", &merge_base]); if !target_paths.is_empty() { git.arg("--").args(target_paths); diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs index bd18eb35c1f2b..e9acb93363e76 100644 --- a/src/bootstrap/src/utils/metrics.rs +++ b/src/bootstrap/src/utils/metrics.rs @@ -13,7 +13,7 @@ use build_helper::metrics::{ JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, Test, TestOutcome, TestSuite, TestSuiteMetadata, }; -use sysinfo::System; +use sysinfo::{CpuRefreshKind, RefreshKind, System}; use crate::core::builder::{Builder, Step}; use crate::utils::helpers::t; @@ -55,7 +55,9 @@ impl BuildMetrics { finished_steps: Vec::new(), running_steps: Vec::new(), - system_info: System::new(), + system_info: System::new_with_specifics( + RefreshKind::new().with_cpu(CpuRefreshKind::everything()), + ), timer_start: None, invocation_timer_start: Instant::now(), invocation_start: SystemTime::now(), @@ -77,7 +79,7 @@ impl BuildMetrics { self.collect_stats(&mut *state); } - state.system_info.refresh_cpu(); + state.system_info.refresh_cpu_usage(); state.timer_start = Some(Instant::now()); state.running_steps.push(StepMetrics { @@ -110,7 +112,7 @@ impl BuildMetrics { state.running_steps.last_mut().unwrap().children.push(step); // Start collecting again for the parent step. - state.system_info.refresh_cpu(); + state.system_info.refresh_cpu_usage(); state.timer_start = Some(Instant::now()); } } @@ -148,7 +150,7 @@ impl BuildMetrics { let elapsed = state.timer_start.unwrap().elapsed(); step.duration_excluding_children_sec += elapsed; - state.system_info.refresh_cpu(); + state.system_info.refresh_cpu_usage(); let cpu = state.system_info.cpus().iter().map(|p| p.cpu_usage()).sum::(); step.cpu_usage_time_sec += cpu as f64 / 100.0 * elapsed.as_secs_f64(); } @@ -159,8 +161,9 @@ impl BuildMetrics { let dest = build.out.join("metrics.json"); - let mut system = System::new(); - system.refresh_cpu(); + let mut system = + System::new_with_specifics(RefreshKind::new().with_cpu(CpuRefreshKind::everything())); + system.refresh_cpu_usage(); system.refresh_memory(); let system_stats = JsonInvocationSystemStats { diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 2621e9a603185..1b98d54169338 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -24,7 +24,8 @@ RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y --no # Needed for apt-key to work: dirmngr \ gpg-agent \ - g++-9-arm-linux-gnueabi + g++-9-arm-linux-gnueabi \ + g++-11-riscv64-linux-gnu RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486 RUN add-apt-repository -y 'deb https://apt.dilos.org/dilos dilos2 main' @@ -73,6 +74,10 @@ RUN env \ CC=arm-linux-gnueabi-gcc-9 CFLAGS="-march=armv7-a" \ CXX=arm-linux-gnueabi-g++-9 CXXFLAGS="-march=armv7-a" \ bash musl.sh armv7 && \ + env \ + CC=riscv64-linux-gnu-gcc-11 \ + CXX=riscv64-linux-gnu-g++-11 \ + bash musl.sh riscv64gc && \ rm -rf /build/* WORKDIR /tmp @@ -125,6 +130,7 @@ ENV TARGETS=$TARGETS,x86_64-unknown-none ENV TARGETS=$TARGETS,aarch64-unknown-uefi ENV TARGETS=$TARGETS,i686-unknown-uefi ENV TARGETS=$TARGETS,x86_64-unknown-uefi +ENV TARGETS=$TARGETS,riscv64gc-unknown-linux-musl # As per https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1300211 # we need asm in the search path for gcc-9 (for gnux32) but not in the search path of the @@ -132,7 +138,11 @@ ENV TARGETS=$TARGETS,x86_64-unknown-uefi # Luckily one of the folders is /usr/local/include so symlink /usr/include/x86_64-linux-gnu/asm there RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/local/include/asm +# musl-gcc can't find libgcc_s.so.1 since it doesn't use the standard search paths. +RUN ln -s /usr/riscv64-linux-gnu/lib/libgcc_s.so.1 /usr/lib/gcc-cross/riscv64-linux-gnu/11/ + ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-llvm-bitcode-linker --disable-docs \ - --musl-root-armv7=/musl-armv7 + --musl-root-armv7=/musl-armv7 \ + --musl-root-riscv64gc=/musl-riscv64gc ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 3acc09e0b9b1a..d690aac27fae6 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -65,4 +65,8 @@ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ make -C linux LLVM=1 -j$(($(nproc) + 1)) \ samples/rust/rust_minimal.o \ samples/rust/rust_print.o \ - drivers/net/phy/ax88796b_rust.o + drivers/net/phy/ax88796b_rust.o \ + rust/doctests_kernel_generated.o + +make -C linux LLVM=1 -j$(($(nproc) + 1)) \ + rustdoc diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 292b6032f8493..467fd6f43e48f 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -65,6 +65,8 @@ - [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/riscv32-unknown-none-elf.md) + - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) + - [riscv64gc-unknown-linux-musl](platform-support/riscv64gc-unknown-linux-musl.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) @@ -74,6 +76,7 @@ - [*-unknown-openbsd](platform-support/openbsd.md) - [*-unknown-redox](platform-support/redox.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) + - [\*-wrs-vxworks](platform-support/vxworks.md) - [wasm32-wasip1](platform-support/wasm32-wasip1.md) - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md) - [wasm32-wasip2](platform-support/wasm32-wasip2.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 0c7f4e7bf1b43..7fd1808a1f026 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -97,7 +97,8 @@ target | notes `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) `powerpc64le-unknown-linux-gnu` | PPC64LE Linux (kernel 3.10, glibc 2.17) -`riscv64gc-unknown-linux-gnu` | RISC-V Linux (kernel 4.20, glibc 2.29) +[`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) +[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3) `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17) `x86_64-unknown-freebsd` | 64-bit FreeBSD `x86_64-unknown-illumos` | illumos @@ -263,7 +264,7 @@ target | std | host | notes [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD [`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS `aarch64-uwp-windows-msvc` | ✓ | | -`aarch64-wrs-vxworks` | ? | | +[`aarch64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | ARM64 VxWorks OS `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian) @@ -281,7 +282,7 @@ target | std | host | notes [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat `armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float -`armv7-wrs-vxworks-eabihf` | ? | | Armv7-A for VxWorks +[`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ? | | Armv7-A for VxWorks [`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3 [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat [`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat @@ -307,7 +308,7 @@ target | std | host | notes `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] `i686-uwp-windows-msvc` | ✓ | | [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] -`i686-wrs-vxworks` | ? | | [^x86_32-floats-return-ABI] +[`i686-wrs-vxworks`](platform-support/vxworks.md) | ? | | [^x86_32-floats-return-ABI] [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 @@ -333,8 +334,8 @@ target | std | host | notes `powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | -`powerpc-wrs-vxworks-spe` | ? | | -`powerpc-wrs-vxworks` | ? | | +[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ? | | +[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ? | | `powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) `powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD `powerpc-unknown-freebsd` | | | PowerPC FreeBSD @@ -354,7 +355,6 @@ target | std | host | notes [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia -`riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.3) [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 [`riscv64-linux-android`](platform-support/android.md) | | | RISC-V 64-bit Android @@ -383,7 +383,7 @@ target | std | host | notes `x86_64-uwp-windows-gnu` | ✓ | | `x86_64-uwp-windows-msvc` | ✓ | | [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support -`x86_64-wrs-vxworks` | ? | | +[`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ? | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`xtensa-esp32-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32 diff --git a/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md new file mode 100644 index 0000000000000..1acc0584be91b --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-gnu.md @@ -0,0 +1,129 @@ +# `riscv64gc-unknown-linux-gnu` + +**Tier: 2 (with Host Tools)** + +RISC-V targets using the *RV64I* base instruction set with the *G* collection of extensions, as well as the *C* extension. + + +## Target maintainers + +- Kito Cheng, , [@kito-cheng](https://github.com/kito-cheng) +- Michael Maitland, , [@michaelmaitland](https://github.com/michaelmaitland) +- Robin Randhawa, , [@robin-randhawa-sifive](https://github.com/robin-randhawa-sifive) +- Craig Topper, , [@topperc](https://github.com/topperc) + +## Requirements + +This target requires: + +* Linux Kernel version 4.20 or later +* glibc 2.17 or later + + +## Building the target + +These targets are distributed through `rustup`, and otherwise require no +special configuration. + +If you need to build your own Rust for some reason though, the targets can be +enabled in `config.toml`. For example: + +```toml +[build] +target = ["riscv64gc-unknown-linux-gnu"] +``` + + +## Building Rust programs + + +On a RISC-V host, the `riscv64gc-unknown-linux-gnu` target should be automatically +installed and used by default. + +On a non-RISC-V host, add the target: + +```bash +rustup target add riscv64gc-unknown-linux-gnu +``` + +Then cross compile crates with: + +```bash +cargo build --target riscv64gc-unknown-linux-gnu +``` + + +## Testing + +There are no special requirements for testing and running the targets. +For testing cross builds on the host, please refer to the "Cross-compilation +toolchains and C code" +section below. + + +## Cross-compilation toolchains and C code + +A RISC-V toolchain can be obtained for Windows/Mac/Linux from the +[`riscv-gnu-toolchain`](https://github.com/riscv-collab/riscv-gnu-toolchain) +repostory. Binaries are available via +[embecosm](https://www.embecosm.com/resources/tool-chain-downloads/#riscv-linux), +and may also be available from your OS's package manager. + +On Ubuntu, a RISC-V toolchain can be installed with: + +```bash +apt install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libc6-dev-riscv64-cross +``` + +Depending on your system, you may need to configure the target to use the GNU +GCC linker. To use it, add the following to your `.cargo/config.toml`: + +```toml +[target.riscv64gc-unknown-linux-gnu] +linker = "riscv64-linux-gnu-gcc" +``` + +If your `riscv64-linux-gnu-*` toolchain is not in your `PATH` you may need to +configure additional settings: + +```toml +[target.riscv64gc-unknown-linux-gnu] +# Adjust the paths to point at your toolchain +cc = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-gcc" +cxx = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-g++" +ar = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-ar" +ranlib = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-ranlib" +linker = "/TOOLCHAIN_PATH/bin/riscv64-linux-gnu-gcc" +``` + +To test cross compiled binaries on a non-RISCV-V host, you can use +[`qemu`](https://www.qemu.org/docs/master/system/target-riscv.html). +On Ubuntu, a RISC-V emulator can be obtained with: + +```bash +apt install qemu-system-riscv64 +``` + +Then, in `.cargo/config.toml` set the `runner`: + +```toml +[target.riscv64gc-unknown-linux-gnu] +runner = "qemu-riscv64-static -L /usr/riscv64-linux-gnu -cpu rv64" +``` + +On Mac and Linux, it's also possible to use +[`lima`](https://github.com/lima-vm/lima) to emulate RISC-V in a similar way to +how WSL2 works on Windows: + +```bash +limactl start template://riscv +limactl shell riscv +``` + +Using [Docker (with BuildKit)](https://docs.docker.com/build/buildkit/) the +[`riscv64/ubuntu`](https://hub.docker.com/r/riscv64/ubuntu) image can be used +to buiild or run `riscv64gc-unknown-linux-gnu` binaries. + +```bash +docker run --platform linux/riscv64 -ti --rm --mount "type=bind,src=$(pwd),dst=/checkout" riscv64/ubuntu bash +``` diff --git a/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-musl.md new file mode 100644 index 0000000000000..5b3dc68303803 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv64gc-unknown-linux-musl.md @@ -0,0 +1,47 @@ +# riscv64gc-unknown-linux-musl + +**Tier: 2** + +Target for RISC-V Linux programs using musl libc. + +## Target maintainers + +- [@Amanieu](https://github.com/Amanieu) +- [@kraj](https://github.com/kraj) + +## Requirements + +Building the target itself requires a RISC-V compiler that is supported by `cc-rs`. + +## Building the target + +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["riscv64gc-unknown-linux-musl"] +``` + +Make sure your C compiler is included in `$PATH`, then add it to the `config.toml`: + +```toml +[target.riscv64gc-unknown-linux-musl] +cc = "riscv64-linux-gnu-gcc" +cxx = "riscv64-linux-gnu-g++" +ar = "riscv64-linux-gnu-ar" +linker = "riscv64-linux-gnu-gcc" +``` + +## Building Rust programs + +This target are distributed through `rustup`, and otherwise require no +special configuration. + +## Cross-compilation + +This target can be cross-compiled from any host. + +## Testing + +This target can be tested as normal with `x.py` on a RISC-V host or via QEMU +emulation. diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md new file mode 100644 index 0000000000000..c0a818558e0bf --- /dev/null +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -0,0 +1,51 @@ +# `*-wrs-vxworks` + +**Tier: 3** + +Targets for the VxWorks operating +system. + +Target triplets available: + +- `x86_64-wrs-vxworks` +- `aarch64-wrs-vxworks` +- `i686-wrs-vxworks` +- `armv7-wrs-vxworks-eabihf` +- `powerpc-wrs-vxworks` +- `powerpc-wrs-vxworks-spe` + +## Target maintainers + +- B I Mohammed Abbas ([@biabbas](https://github.com/biabbas)) + +## Requirements + +Rust for each target can be cross-compiled with its specific target vsb configuration. Std support is added but not yet fully tested. + +## Building the target + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`. In addition the workbench and wr-cc have to configured and activated. + +```toml +[build] +build-stage = 1 +target = [ + "", + "x86_64-wrs-vxworks", + "aarch64-wrs-vxworks", + "i686-wrs-vxworks", + "armv7-wrs-vxworks-eabihf", + "powerpc-wrs-vxworks", + "powerpc-wrs-vxworks-spe", +] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for VxWorks. + +The easiest way to build and test programs for VxWorks is to use the shipped rustc and cargo in VxWorks workbench, following the official windriver guidelines. + +## Cross-compilation toolchains and C code + +The target supports C code. Pre-compiled C toolchains can be found in provided VxWorks workbench. diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index dfd7414652fa7..b3fccbf6456e0 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -8,7 +8,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } -rinja = { version = "0.2", default-features = false, features = ["config"] } +rinja = { version = "0.3", default-features = false, features = ["config"] } base64 = "0.21.7" itertools = "0.12" indexmap = "2" diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index d92bc8456649f..f8953f0ebcfb5 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{sym, Symbol}; use thin_vec::{thin_vec, ThinVec}; use {rustc_ast as ast, rustc_hir as hir}; @@ -792,11 +792,7 @@ fn build_macro( fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics { for pred in &mut g.where_predicates { match *pred { - clean::WherePredicate::BoundPredicate { - ty: clean::Generic(ref s), - ref mut bounds, - .. - } if *s == kw::SelfUpper => { + clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref mut bounds, .. } => { bounds.retain(|bound| match bound { clean::GenericBound::TraitBound(clean::PolyTrait { trait_, .. }, _) => { trait_.def_id() != trait_did @@ -812,13 +808,13 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: clean::WherePredicate::BoundPredicate { ty: clean::QPath(box clean::QPathData { - self_type: clean::Generic(ref s), + self_type: clean::Generic(_), trait_: Some(trait_), .. }), bounds, .. - } => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did), + } => !bounds.is_empty() && trait_.def_id() != trait_did, _ => true, }); g @@ -832,9 +828,7 @@ fn separate_supertrait_bounds( ) -> (clean::Generics, Vec) { let mut ty_bounds = Vec::new(); g.where_predicates.retain(|pred| match *pred { - clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds, .. } - if *s == kw::SelfUpper => - { + clean::WherePredicate::BoundPredicate { ty: clean::SelfTy, ref bounds, .. } => { ty_bounds.extend(bounds.iter().cloned()); false } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0dfda83c25dad..cffadc7c10a91 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1351,11 +1351,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let self_arg_ty = tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder(); if self_arg_ty == self_ty { - item.decl.inputs.values[0].type_ = Generic(kw::SelfUpper); + item.decl.inputs.values[0].type_ = SelfTy; } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { if ty == self_ty { match item.decl.inputs.values[0].type_ { - BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper), + BorrowedRef { ref mut type_, .. } => **type_ = SelfTy, _ => unreachable!(), } } @@ -1439,9 +1439,8 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( if trait_.def_id() != assoc_item.container_id(tcx) { return true; } - match *self_type { - Generic(ref s) if *s == kw::SelfUpper => {} - _ => return true, + if *self_type != SelfTy { + return true; } match &assoc.args { GenericArgs::AngleBracketed { args, constraints } => { @@ -2228,6 +2227,8 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Param(ref p) => { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { ImplTrait(bounds) + } else if p.name == kw::SelfUpper { + SelfTy } else { Generic(p.name) } @@ -2748,10 +2749,9 @@ fn clean_maybe_renamed_item<'tcx>( type_: clean_ty(ty, cx), kind: ConstantKind::Local { body: body_id, def_id }, })), - ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { - bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), - generics: clean_generics(ty.generics, cx), - }), + // clean_ty changes types which reference an OpaqueTy item to instead be + // an ImplTrait, so it's ok to return nothing here. + ItemKind::OpaqueTy(_) => return vec![], ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); @@ -2834,7 +2834,7 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::Use(path, kind) => { return clean_use_statement(item, name, path, kind, cx, &mut FxHashSet::default()); } - _ => unreachable!("not yet converted"), + _ => span_bug!(item.span, "not yet converted"), }; vec![generate_item_with_correct_attrs( diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 739f6eb8cc89d..1d81ae3eb8ba7 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -145,7 +145,6 @@ pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generi // should be handled when cleaning associated types. generics.where_predicates.retain(|pred| { if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred - && *param != rustc_span::symbol::kw::SelfUpper && bounds.iter().any(|b| b.is_sized_bound(cx)) { sized_params.insert(*param); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1ec5f38b6ec0f..4850500a1bfae 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -34,10 +34,9 @@ use thin_vec::ThinVec; use {rustc_ast as ast, rustc_hir as hir}; pub(crate) use self::ItemKind::*; -pub(crate) use self::SelfTy::*; pub(crate) use self::Type::{ Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, - RawPointer, Slice, Tuple, + RawPointer, SelfTy, Slice, Tuple, }; use crate::clean::cfg::Cfg; use crate::clean::clean_middle_path; @@ -824,7 +823,6 @@ pub(crate) enum ItemKind { FunctionItem(Box), ModuleItem(Module), TypeAliasItem(Box), - OpaqueTyItem(OpaqueTy), StaticItem(Static), TraitItem(Box), TraitAliasItem(TraitAlias), @@ -882,7 +880,6 @@ impl ItemKind { | ImportItem(_) | FunctionItem(_) | TypeAliasItem(_) - | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) | TraitAliasItem(_) @@ -916,7 +913,6 @@ impl ItemKind { | ExternCrateItem { .. } | FunctionItem(_) | TypeAliasItem(_) - | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) | TraitAliasItem(_) @@ -1387,8 +1383,8 @@ pub(crate) struct FnDecl { } impl FnDecl { - pub(crate) fn self_type(&self) -> Option { - self.inputs.values.get(0).and_then(|v| v.to_self()) + pub(crate) fn receiver_type(&self) -> Option<&Type> { + self.inputs.values.get(0).and_then(|v| v.to_receiver()) } } @@ -1406,27 +1402,9 @@ pub(crate) struct Argument { pub(crate) is_const: bool, } -#[derive(Clone, PartialEq, Debug)] -pub(crate) enum SelfTy { - SelfValue, - SelfBorrowed(Option, Mutability), - SelfExplicit(Type), -} - impl Argument { - pub(crate) fn to_self(&self) -> Option { - if self.name != kw::SelfLower { - return None; - } - if self.type_.is_self_type() { - return Some(SelfValue); - } - match self.type_ { - BorrowedRef { ref lifetime, mutability, ref type_ } if type_.is_self_type() => { - Some(SelfBorrowed(lifetime.clone(), mutability)) - } - _ => Some(SelfExplicit(self.type_.clone())), - } + pub(crate) fn to_receiver(&self) -> Option<&Type> { + if self.name == kw::SelfLower { Some(&self.type_) } else { None } } } @@ -1480,6 +1458,8 @@ pub(crate) enum Type { DynTrait(Vec, Option), /// A type parameter. Generic(Symbol), + /// The `Self` type. + SelfTy, /// A primitive (aka, builtin) type. Primitive(PrimitiveType), /// A function pointer: `extern "ABI" fn(...) -> ...` @@ -1574,6 +1554,8 @@ impl Type { // If both sides are generic, this returns true. (_, Type::Generic(_)) => true, (Type::Generic(_), _) => false, + // `Self` only matches itself. + (Type::SelfTy, Type::SelfTy) => true, // Paths account for both the path itself and its generics. (Type::Path { path: a }, Type::Path { path: b }) => { a.def_id() == b.def_id() @@ -1645,7 +1627,7 @@ impl Type { pub(crate) fn is_self_type(&self) -> bool { match *self { - Generic(name) => name == kw::SelfUpper, + SelfTy => true, _ => false, } } @@ -1680,13 +1662,16 @@ impl Type { } } - fn inner_def_id(&self, cache: Option<&Cache>) -> Option { + /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. + /// + /// [clean]: crate::clean + pub(crate) fn def_id(&self, cache: &Cache) -> Option { let t: PrimitiveType = match *self { Type::Path { ref path } => return Some(path.def_id()), DynTrait(ref bounds, _) => return bounds.get(0).map(|b| b.trait_.def_id()), - Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), + Primitive(p) => return cache.primitive_locations.get(&p).cloned(), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, - BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache), + BorrowedRef { ref type_, .. } => return type_.def_id(cache), Tuple(ref tys) => { if tys.is_empty() { PrimitiveType::Unit @@ -1699,17 +1684,10 @@ impl Type { Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, - QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache), - Generic(_) | Infer | ImplTrait(_) => return None, + QPath(box QPathData { ref self_type, .. }) => return self_type.def_id(cache), + Generic(_) | SelfTy | Infer | ImplTrait(_) => return None, }; - cache.and_then(|c| Primitive(t).def_id(c)) - } - - /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. - /// - /// [clean]: crate::clean - pub(crate) fn def_id(&self, cache: &Cache) -> Option { - self.inner_def_id(Some(cache)) + Primitive(t).def_id(cache) } } @@ -2339,12 +2317,6 @@ pub(crate) struct TypeAlias { pub(crate) item_type: Option, } -#[derive(Clone, Debug)] -pub(crate) struct OpaqueTy { - pub(crate) bounds: Vec, - pub(crate) generics: Generics, -} - #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct BareFunctionDecl { pub(crate) safety: hir::Safety, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 2dd3041ab4c61..68266f3506a01 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -468,7 +468,7 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { match path.res { Res::PrimTy(p) => Primitive(PrimitiveType::from(p)), Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } if path.segments.len() == 1 => { - Generic(kw::SelfUpper) + Type::SelfTy } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name), _ => { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 3e1271f198ca7..fea31e7ecbc9a 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -195,6 +195,7 @@ pub(crate) fn create_config( lint_cap, scrape_examples_options, expanded_args, + remap_path_prefix, .. }: RustdocOptions, RenderOptions { document_private, .. }: &RenderOptions, @@ -247,6 +248,7 @@ pub(crate) fn create_config( describe_lints, crate_name, test, + remap_path_prefix, ..Options::default() }; diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index beb7686e29c56..bf82c911f290f 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -77,7 +77,6 @@ pub(crate) trait DocFolder: Sized { ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) - | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 9f284486616a0..947bae99305a8 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -260,153 +260,21 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Index this method for searching later on. - if let Some(s) = item.name.or_else(|| { - if item.is_stripped() { - None - } else if let clean::ImportItem(ref i) = *item.kind - && let clean::ImportKind::Simple(s) = i.kind - { - Some(s) - } else { - None - } - }) { - let (parent, is_inherent_impl_item) = match *item.kind { - clean::StrippedItem(..) => ((None, None), false), - clean::AssocConstItem(..) | clean::AssocTypeItem(..) - if self - .cache - .parent_stack - .last() - .is_some_and(|parent| parent.is_trait_impl()) => + let search_name = if !item.is_stripped() { + item.name.or_else(|| { + if let clean::ImportItem(ref i) = *item.kind + && let clean::ImportKind::Simple(s) = i.kind { - // skip associated items in trait impls - ((None, None), false) - } - clean::TyMethodItem(..) - | clean::TyAssocConstItem(..) - | clean::TyAssocTypeItem(..) - | clean::StructFieldItem(..) - | clean::VariantItem(..) => ( - ( - Some( - self.cache - .parent_stack - .last() - .expect("parent_stack is empty") - .item_id() - .expect_def_id(), - ), - Some(&self.cache.stack[..self.cache.stack.len() - 1]), - ), - false, - ), - clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { - if self.cache.parent_stack.is_empty() { - ((None, None), false) - } else { - let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); - let did = match &*last { - ParentStackItem::Impl { - // impl Trait for &T { fn method(self); } - // - // When generating a function index with the above shape, we want it - // associated with `T`, not with the primitive reference type. It should - // show up as `T::method`, rather than `reference::method`, in the search - // results page. - for_: clean::Type::BorrowedRef { type_, .. }, - .. - } => type_.def_id(&self.cache), - ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache), - ParentStackItem::Type(item_id) => item_id.as_def_id(), - }; - let path = did - .and_then(|did| self.cache.paths.get(&did)) - // The current stack not necessarily has correlation - // for where the type was defined. On the other - // hand, `paths` always has the right - // information if present. - .map(|(fqp, _)| &fqp[..fqp.len() - 1]); - ((did, path), true) - } - } - _ => ((None, Some(&*self.cache.stack)), false), - }; - - match parent { - (parent, Some(path)) if is_inherent_impl_item || !self.cache.stripped_mod => { - debug_assert!(!item.is_stripped()); - - // A crate has a module at its root, containing all items, - // which should not be indexed. The crate-item itself is - // inserted later on when serializing the search-index. - if item.item_id.as_def_id().is_some_and(|idx| !idx.is_crate_root()) - && let ty = item.type_() - && (ty != ItemType::StructField - || u16::from_str_radix(s.as_str(), 10).is_err()) - { - let desc = - short_markdown_summary(&item.doc_value(), &item.link_names(self.cache)); - // For searching purposes, a re-export is a duplicate if: - // - // - It's either an inline, or a true re-export - // - It's got the same name - // - Both of them have the same exact path - let defid = (match &*item.kind { - &clean::ItemKind::ImportItem(ref import) => import.source.did, - _ => None, - }) - .or_else(|| item.item_id.as_def_id()); - // In case this is a field from a tuple struct, we don't add it into - // the search index because its name is something like "0", which is - // not useful for rustdoc search. - self.cache.search_index.push(IndexItem { - ty, - defid, - name: s, - path: join_with_double_colon(path), - desc, - parent, - parent_idx: None, - exact_path: None, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = - self.cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - search_type: get_function_type_for_search( - &item, - self.tcx, - clean_impl_generics(self.cache.parent_stack.last()).as_ref(), - parent, - self.cache, - ), - aliases: item.attrs.get_doc_aliases(), - deprecation: item.deprecation(self.tcx), - }); - } + Some(s) + } else { + None } - (Some(parent), None) if is_inherent_impl_item => { - // We have a parent, but we don't know where they're - // defined yet. Wait for later to index this item. - let impl_generics = clean_impl_generics(self.cache.parent_stack.last()); - self.cache.orphan_impl_items.push(OrphanImplItem { - parent, - item: item.clone(), - impl_generics, - impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = - self.cache.parent_stack.last() - { - item_id.as_def_id() - } else { - None - }, - }); - } - _ => {} - } + }) + } else { + None + }; + if let Some(name) = search_name { + add_item_to_search_index(self.tcx, &mut self.cache, &item, name) } // Keep track of the fully qualified path for this item. @@ -442,16 +310,16 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // `public_items` map, so we can skip inserting into the // paths map if there was already an entry present and we're // not a public item. - if !self.cache.paths.contains_key(&item.item_id.expect_def_id()) + let item_def_id = item.item_id.expect_def_id(); + if !self.cache.paths.contains_key(&item_def_id) || self .cache .effective_visibilities - .is_directly_public(self.tcx, item.item_id.expect_def_id()) + .is_directly_public(self.tcx, item_def_id) { - self.cache.paths.insert( - item.item_id.expect_def_id(), - (self.cache.stack.clone(), item.type_()), - ); + self.cache + .paths + .insert(item_def_id, (self.cache.stack.clone(), item.type_())); } } } @@ -463,7 +331,6 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { clean::ExternCrateItem { .. } | clean::ImportItem(..) - | clean::OpaqueTyItem(..) | clean::ImplItem(..) | clean::TyMethodItem(..) | clean::MethodItem(..) @@ -514,9 +381,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { && adt.is_fundamental() { for ty in generics { - if let Some(did) = ty.def_id(self.cache) { - dids.insert(did); - } + dids.extend(ty.def_id(self.cache)); } } } @@ -529,32 +394,26 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { .primitive_type() .and_then(|t| self.cache.primitive_locations.get(&t).cloned()); - if let Some(did) = did { - dids.insert(did); - } + dids.extend(did); } } if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { for bound in generics { - if let Some(did) = bound.def_id(self.cache) { - dids.insert(did); - } + dids.extend(bound.def_id(self.cache)); } } let impl_item = Impl { impl_item: item }; - if impl_item.trait_did().map_or(true, |d| self.cache.traits.contains_key(&d)) { + let impl_did = impl_item.def_id(); + let trait_did = impl_item.trait_did(); + if trait_did.map_or(true, |d| self.cache.traits.contains_key(&d)) { for did in dids { - if self.impl_ids.entry(did).or_default().insert(impl_item.def_id()) { - self.cache - .impls - .entry(did) - .or_insert_with(Vec::new) - .push(impl_item.clone()); + if self.impl_ids.entry(did).or_default().insert(impl_did) { + self.cache.impls.entry(did).or_default().push(impl_item.clone()); } } } else { - let trait_did = impl_item.trait_did().expect("no trait did"); + let trait_did = trait_did.expect("no trait did"); self.cache.orphan_trait_impls.push((trait_did, dids, impl_item)); } None @@ -573,6 +432,152 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } } +fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::Item, name: Symbol) { + // Item has a name, so it must also have a DefId (can't be an impl, let alone a blanket or auto impl). + let item_def_id = item.item_id.as_def_id().unwrap(); + let (parent_did, parent_path) = match *item.kind { + clean::StrippedItem(..) => return, + clean::AssocConstItem(..) | clean::AssocTypeItem(..) + if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) => + { + // skip associated items in trait impls + return; + } + clean::TyMethodItem(..) + | clean::TyAssocConstItem(..) + | clean::TyAssocTypeItem(..) + | clean::StructFieldItem(..) + | clean::VariantItem(..) => { + // Don't index if containing module is stripped (i.e., private), + // or if item is tuple struct/variant field (name is a number -> not useful for search). + if cache.stripped_mod + || item.type_() == ItemType::StructField + && name.as_str().chars().all(|c| c.is_digit(10)) + { + return; + } + let parent_did = + cache.parent_stack.last().expect("parent_stack is empty").item_id().expect_def_id(); + let parent_path = &cache.stack[..cache.stack.len() - 1]; + (Some(parent_did), parent_path) + } + clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { + let last = cache.parent_stack.last().expect("parent_stack is empty 2"); + let parent_did = match &*last { + // impl Trait for &T { fn method(self); } + // + // When generating a function index with the above shape, we want it + // associated with `T`, not with the primitive reference type. It should + // show up as `T::method`, rather than `reference::method`, in the search + // results page. + ParentStackItem::Impl { for_: clean::Type::BorrowedRef { type_, .. }, .. } => { + type_.def_id(&cache) + } + ParentStackItem::Impl { for_, .. } => for_.def_id(&cache), + ParentStackItem::Type(item_id) => item_id.as_def_id(), + }; + let Some(parent_did) = parent_did else { return }; + // The current stack reflects the CacheBuilder's recursive + // walk over HIR. For associated items, this is the module + // where the `impl` block is defined. That's an implementation + // detail that we don't want to affect the search engine. + // + // In particular, you can arrange things like this: + // + // #![crate_name="me"] + // mod private_mod { + // impl Clone for MyThing { fn clone(&self) -> MyThing { MyThing } } + // } + // pub struct MyThing; + // + // When that happens, we need to: + // - ignore the `cache.stripped_mod` flag, since the Clone impl is actually + // part of the public API even though it's defined in a private module + // - present the method as `me::MyThing::clone`, its publicly-visible path + // - deal with the fact that the recursive walk hasn't actually reached `MyThing` + // until it's already past `private_mod`, since that's first, and doesn't know + // yet if `MyThing` will actually be public or not (it could be re-exported) + // + // We accomplish the last two points by recording children of "orphan impls" + // in a field of the cache whose elements are added to the search index later, + // after cache building is complete (see `handle_orphan_impl_child`). + match cache.paths.get(&parent_did) { + Some((fqp, _)) => (Some(parent_did), &fqp[..fqp.len() - 1]), + None => { + handle_orphan_impl_child(cache, item, parent_did); + return; + } + } + } + _ => { + // Don't index if item is crate root, which is inserted later on when serializing the index. + // Don't index if containing module is stripped (i.e., private), + if item_def_id.is_crate_root() || cache.stripped_mod { + return; + } + (None, &*cache.stack) + } + }; + + debug_assert!(!item.is_stripped()); + + let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); + // For searching purposes, a re-export is a duplicate if: + // + // - It's either an inline, or a true re-export + // - It's got the same name + // - Both of them have the same exact path + let defid = match &*item.kind { + clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), + _ => item_def_id, + }; + let path = join_with_double_colon(parent_path); + let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { + item_id.as_def_id() + } else { + None + }; + let search_type = get_function_type_for_search( + &item, + tcx, + clean_impl_generics(cache.parent_stack.last()).as_ref(), + parent_did, + cache, + ); + let aliases = item.attrs.get_doc_aliases(); + let deprecation = item.deprecation(tcx); + let index_item = IndexItem { + ty: item.type_(), + defid: Some(defid), + name, + path, + desc, + parent: parent_did, + parent_idx: None, + exact_path: None, + impl_id, + search_type, + aliases, + deprecation, + }; + cache.search_index.push(index_item); +} + +/// We have a parent, but we don't know where they're +/// defined yet. Wait for later to index this item. +/// See [`Cache::orphan_impl_items`]. +fn handle_orphan_impl_child(cache: &mut Cache, item: &clean::Item, parent_did: DefId) { + let impl_generics = clean_impl_generics(cache.parent_stack.last()); + let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { + item_id.as_def_id() + } else { + None + }; + let orphan_item = + OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id }; + cache.orphan_impl_items.push(orphan_item); +} + pub(crate) struct OrphanImplItem { pub(crate) parent: DefId, pub(crate) impl_id: Option, diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index c860eb8c6964b..3dcef15b552b2 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -51,7 +51,7 @@ pub(crate) enum ItemType { AssocConst = 19, Union = 20, ForeignType = 21, - OpaqueTy = 22, + // OpaqueTy used to be here, but it was removed in #127276 ProcAttribute = 23, ProcDerive = 24, TraitAlias = 25, @@ -84,7 +84,6 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::EnumItem(..) => ItemType::Enum, clean::FunctionItem(..) => ItemType::Function, clean::TypeAliasItem(..) => ItemType::TypeAlias, - clean::OpaqueTyItem(..) => ItemType::OpaqueTy, clean::StaticItem(..) => ItemType::Static, clean::ConstantItem(..) => ItemType::Constant, clean::TraitItem(..) => ItemType::Trait, @@ -191,7 +190,6 @@ impl ItemType { ItemType::AssocConst => "associatedconstant", ItemType::ForeignType => "foreigntype", ItemType::Keyword => "keyword", - ItemType::OpaqueTy => "opaque", ItemType::ProcAttribute => "attr", ItemType::ProcDerive => "derive", ItemType::TraitAlias => "traitalias", diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index bb5ac303ffd63..b5ab6a35fdb9d 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1006,6 +1006,7 @@ fn fmt_type<'cx>( match *t { clean::Generic(name) => f.write_str(name.as_str()), + clean::SelfTy => f.write_str("Self"), clean::Type::Path { ref path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); @@ -1452,29 +1453,22 @@ impl clean::FnDecl { let last_input_index = self.inputs.values.len().checked_sub(1); for (i, input) in self.inputs.values.iter().enumerate() { - if let Some(selfty) = input.to_self() { + if let Some(selfty) = input.to_receiver() { match selfty { - clean::SelfValue => { + clean::SelfTy => { write!(f, "self")?; } - clean::SelfBorrowed(Some(ref lt), mutability) => { - write!( - f, - "{amp}{lifetime} {mutability}self", - lifetime = lt.print(), - mutability = mutability.print_with_space(), - )?; - } - clean::SelfBorrowed(None, mutability) => { - write!( - f, - "{amp}{mutability}self", - mutability = mutability.print_with_space(), - )?; + clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { + write!(f, "{amp}")?; + match lifetime { + Some(lt) => write!(f, "{lt} ", lt = lt.print())?, + None => {} + } + write!(f, "{mutability}self", mutability = mutability.print_with_space())?; } - clean::SelfExplicit(ref typ) => { + _ => { write!(f, "self: ")?; - typ.print(cx).fmt(f)?; + selfty.print(cx).fmt(f)?; } } } else { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index d72671fbdabeb..586c3c509b4ce 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -60,7 +60,7 @@ use serde::{Serialize, Serializer}; pub(crate) use self::context::*; pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc}; -use crate::clean::{self, ItemId, RenderedLink, SelfTy}; +use crate::clean::{self, ItemId, RenderedLink}; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; @@ -334,7 +334,6 @@ struct AllTypes { macros: FxHashSet, functions: FxHashSet, type_aliases: FxHashSet, - opaque_tys: FxHashSet, statics: FxHashSet, constants: FxHashSet, attribute_macros: FxHashSet, @@ -354,7 +353,6 @@ impl AllTypes { macros: new_set(100), functions: new_set(100), type_aliases: new_set(100), - opaque_tys: new_set(100), statics: new_set(100), constants: new_set(100), attribute_macros: new_set(100), @@ -378,7 +376,6 @@ impl AllTypes { ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)), - ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), ItemType::ProcAttribute => { @@ -418,9 +415,6 @@ impl AllTypes { if !self.type_aliases.is_empty() { sections.insert(ItemSection::TypeAliases); } - if !self.opaque_tys.is_empty() { - sections.insert(ItemSection::OpaqueTypes); - } if !self.statics.is_empty() { sections.insert(ItemSection::Statics); } @@ -474,7 +468,6 @@ impl AllTypes { print_entries(f, &self.functions, ItemSection::Functions); print_entries(f, &self.type_aliases, ItemSection::TypeAliases); print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); - print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes); print_entries(f, &self.statics, ItemSection::Statics); print_entries(f, &self.constants, ItemSection::Constants); } @@ -1381,21 +1374,20 @@ fn render_deref_methods( fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { let self_type_opt = match *item.kind { - clean::MethodItem(ref method, _) => method.decl.self_type(), - clean::TyMethodItem(ref method) => method.decl.self_type(), + clean::MethodItem(ref method, _) => method.decl.receiver_type(), + clean::TyMethodItem(ref method) => method.decl.receiver_type(), _ => None, }; if let Some(self_ty) = self_type_opt { - let (by_mut_ref, by_box, by_value) = match self_ty { - SelfTy::SelfBorrowed(_, mutability) - | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { + let (by_mut_ref, by_box, by_value) = match *self_ty { + clean::Type::BorrowedRef { mutability, .. } => { (mutability == Mutability::Mut, false, false) } - SelfTy::SelfExplicit(clean::Type::Path { path }) => { + clean::Type::Path { ref path } => { (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false) } - SelfTy::SelfValue => (false, false, true), + clean::Type::SelfTy => (false, false, true), _ => (false, false, false), }; @@ -2177,7 +2169,6 @@ pub(crate) enum ItemSection { AssociatedConstants, ForeignTypes, Keywords, - OpaqueTypes, AttributeMacros, DeriveMacros, TraitAliases, @@ -2210,7 +2201,6 @@ impl ItemSection { AssociatedConstants, ForeignTypes, Keywords, - OpaqueTypes, AttributeMacros, DeriveMacros, TraitAliases, @@ -2240,7 +2230,6 @@ impl ItemSection { Self::AssociatedConstants => "associated-consts", Self::ForeignTypes => "foreign-types", Self::Keywords => "keywords", - Self::OpaqueTypes => "opaque-types", Self::AttributeMacros => "attributes", Self::DeriveMacros => "derives", Self::TraitAliases => "trait-aliases", @@ -2270,7 +2259,6 @@ impl ItemSection { Self::AssociatedConstants => "Associated Constants", Self::ForeignTypes => "Foreign Types", Self::Keywords => "Keywords", - Self::OpaqueTypes => "Opaque Types", Self::AttributeMacros => "Attribute Macros", Self::DeriveMacros => "Derive Macros", Self::TraitAliases => "Trait Aliases", @@ -2301,7 +2289,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection { ItemType::AssocConst => ItemSection::AssociatedConstants, ItemType::ForeignType => ItemSection::ForeignTypes, ItemType::Keyword => ItemSection::Keywords, - ItemType::OpaqueTy => ItemSection::OpaqueTypes, ItemType::ProcAttribute => ItemSection::AttributeMacros, ItemType::ProcDerive => ItemSection::DeriveMacros, ItemType::TraitAlias => ItemSection::TraitAliases, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index eec6df9dd2013..d8b111471b85e 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -202,7 +202,6 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf clean::ConstantItem(..) => "Constant ", clean::ForeignTypeItem => "Foreign Type ", clean::KeywordItem => "Keyword ", - clean::OpaqueTyItem(..) => "Opaque Type ", clean::TraitAliasItem(..) => "Trait Alias ", _ => { // We don't generate pages for any other type. @@ -270,7 +269,6 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf clean::ConstantItem(ci) => item_constant(buf, cx, item, &ci.generics, &ci.type_, &ci.kind), clean::ForeignTypeItem => item_foreign_type(buf, cx, item), clean::KeywordItem => item_keyword(buf, cx, item), - clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e), clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), _ => { // We don't generate pages for any other type. @@ -1197,35 +1195,6 @@ fn item_trait_alias( .unwrap(); } -fn item_opaque_ty( - w: &mut impl fmt::Write, - cx: &mut Context<'_>, - it: &clean::Item, - t: &clean::OpaqueTy, -) { - wrap_item(w, |w| { - write!( - w, - "{attrs}type {name}{generics}{where_clause} = impl {bounds};", - attrs = render_attributes_in_pre(it, "", cx), - name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - bounds = bounds(&t.bounds, false, cx), - ) - .unwrap(); - }); - - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) - .unwrap(); -} - fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { wrap_item(w, |w| { write!( diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 9f195da43c609..8a12bdef69bf4 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -793,7 +793,11 @@ fn get_index_type_id( } } // Not supported yet - clean::Type::Pat(..) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, + clean::Type::Pat(..) + | clean::Generic(_) + | clean::SelfTy + | clean::ImplTrait(_) + | clean::Infer => None, } } @@ -846,34 +850,70 @@ fn simplify_fn_type<'tcx, 'a>( // If this argument is a type parameter and not a trait bound or a type, we need to look // for its bounds. - if let Type::Generic(arg_s) = *arg { - // First we check if the bounds are in a `where` predicate... - let mut type_bounds = Vec::new(); - for where_pred in generics.where_predicates.iter().filter(|g| match g { - WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s, - _ => false, - }) { - let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); - for bound in bounds.iter() { - if let Some(path) = bound.get_trait_path() { - let ty = Type::Path { path }; - simplify_fn_type( - self_, - generics, - &ty, - tcx, - recurse + 1, - &mut type_bounds, - rgen, - is_return, - cache, - ); + match *arg { + Type::Generic(arg_s) => { + // First we check if the bounds are in a `where` predicate... + let mut type_bounds = Vec::new(); + for where_pred in generics.where_predicates.iter().filter(|g| match g { + WherePredicate::BoundPredicate { ty, .. } => *ty == *arg, + _ => false, + }) { + let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); + for bound in bounds.iter() { + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + simplify_fn_type( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut type_bounds, + rgen, + is_return, + cache, + ); + } + } + } + // Otherwise we check if the trait bounds are "inlined" like `T: Option`... + if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) { + for bound in bound.get_bounds().unwrap_or(&[]) { + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + simplify_fn_type( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut type_bounds, + rgen, + is_return, + cache, + ); + } } } + if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) { + res.push(RenderType { + id: Some(RenderTypeId::Index(*idx)), + generics: None, + bindings: None, + }); + } else { + let idx = -isize::try_from(rgen.len() + 1).unwrap(); + rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds)); + res.push(RenderType { + id: Some(RenderTypeId::Index(idx)), + generics: None, + bindings: None, + }); + } } - // Otherwise we check if the trait bounds are "inlined" like `T: Option`... - if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) { - for bound in bound.get_bounds().unwrap_or(&[]) { + Type::ImplTrait(ref bounds) => { + let mut type_bounds = Vec::new(); + for bound in bounds { if let Some(path) = bound.get_trait_path() { let ty = Type::Path { path }; simplify_fn_type( @@ -889,84 +929,22 @@ fn simplify_fn_type<'tcx, 'a>( ); } } - } - if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) { - res.push(RenderType { - id: Some(RenderTypeId::Index(*idx)), - generics: None, - bindings: None, - }); - } else { - let idx = -isize::try_from(rgen.len() + 1).unwrap(); - rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds)); - res.push(RenderType { - id: Some(RenderTypeId::Index(idx)), - generics: None, - bindings: None, - }); - } - } else if let Type::ImplTrait(ref bounds) = *arg { - let mut type_bounds = Vec::new(); - for bound in bounds { - if let Some(path) = bound.get_trait_path() { - let ty = Type::Path { path }; - simplify_fn_type( - self_, - generics, - &ty, - tcx, - recurse + 1, - &mut type_bounds, - rgen, - is_return, - cache, - ); + if is_return && !type_bounds.is_empty() { + // In return position, `impl Trait` is a unique thing. + res.push(RenderType { id: None, generics: Some(type_bounds), bindings: None }); + } else { + // In parameter position, `impl Trait` is the same as an unnamed generic parameter. + let idx = -isize::try_from(rgen.len() + 1).unwrap(); + rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds)); + res.push(RenderType { + id: Some(RenderTypeId::Index(idx)), + generics: None, + bindings: None, + }); } } - if is_return && !type_bounds.is_empty() { - // In parameter position, `impl Trait` is a unique thing. - res.push(RenderType { id: None, generics: Some(type_bounds), bindings: None }); - } else { - // In parameter position, `impl Trait` is the same as an unnamed generic parameter. - let idx = -isize::try_from(rgen.len() + 1).unwrap(); - rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds)); - res.push(RenderType { - id: Some(RenderTypeId::Index(idx)), - generics: None, - bindings: None, - }); - } - } else if let Type::Slice(ref ty) = *arg { - let mut ty_generics = Vec::new(); - simplify_fn_type( - self_, - generics, - &ty, - tcx, - recurse + 1, - &mut ty_generics, - rgen, - is_return, - cache, - ); - res.push(get_index_type(arg, ty_generics, rgen)); - } else if let Type::Array(ref ty, _) = *arg { - let mut ty_generics = Vec::new(); - simplify_fn_type( - self_, - generics, - &ty, - tcx, - recurse + 1, - &mut ty_generics, - rgen, - is_return, - cache, - ); - res.push(get_index_type(arg, ty_generics, rgen)); - } else if let Type::Tuple(ref tys) = *arg { - let mut ty_generics = Vec::new(); - for ty in tys { + Type::Slice(ref ty) => { + let mut ty_generics = Vec::new(); simplify_fn_type( self_, generics, @@ -978,15 +956,14 @@ fn simplify_fn_type<'tcx, 'a>( is_return, cache, ); + res.push(get_index_type(arg, ty_generics, rgen)); } - res.push(get_index_type(arg, ty_generics, rgen)); - } else if let Type::BareFunction(ref bf) = *arg { - let mut ty_generics = Vec::new(); - for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) { + Type::Array(ref ty, _) => { + let mut ty_generics = Vec::new(); simplify_fn_type( self_, generics, - ty, + &ty, tcx, recurse + 1, &mut ty_generics, @@ -994,62 +971,11 @@ fn simplify_fn_type<'tcx, 'a>( is_return, cache, ); + res.push(get_index_type(arg, ty_generics, rgen)); } - // The search index, for simplicity's sake, represents fn pointers and closures - // the same way: as a tuple for the parameters, and an associated type for the - // return type. - let mut ty_output = Vec::new(); - simplify_fn_type( - self_, - generics, - &bf.decl.output, - tcx, - recurse + 1, - &mut ty_output, - rgen, - is_return, - cache, - ); - let ty_bindings = vec![(RenderTypeId::AssociatedType(sym::Output), ty_output)]; - res.push(RenderType { - id: get_index_type_id(&arg, rgen), - bindings: Some(ty_bindings), - generics: Some(ty_generics), - }); - } else if let Type::BorrowedRef { lifetime: _, mutability, ref type_ } = *arg { - let mut ty_generics = Vec::new(); - if mutability.is_mut() { - ty_generics.push(RenderType { - id: Some(RenderTypeId::Mut), - generics: None, - bindings: None, - }); - } - simplify_fn_type( - self_, - generics, - &type_, - tcx, - recurse + 1, - &mut ty_generics, - rgen, - is_return, - cache, - ); - res.push(get_index_type(arg, ty_generics, rgen)); - } else { - // This is not a type parameter. So for example if we have `T, U: Option`, and we're - // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't. - // - // So in here, we can add it directly and look for its own type parameters (so for `Option`, - // we will look for them but not for `T`). - let mut ty_generics = Vec::new(); - let mut ty_constraints = Vec::new(); - if let Some(arg_generics) = arg.generic_args() { - for ty in arg_generics.into_iter().filter_map(|param| match param { - clean::GenericArg::Type(ty) => Some(ty), - _ => None, - }) { + Type::Tuple(ref tys) => { + let mut ty_generics = Vec::new(); + for ty in tys { simplify_fn_type( self_, generics, @@ -1062,94 +988,181 @@ fn simplify_fn_type<'tcx, 'a>( cache, ); } - for constraint in arg_generics.constraints() { - simplify_fn_constraint( + res.push(get_index_type(arg, ty_generics, rgen)); + } + Type::BareFunction(ref bf) => { + let mut ty_generics = Vec::new(); + for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) { + simplify_fn_type( self_, generics, - &constraint, + ty, tcx, recurse + 1, - &mut ty_constraints, + &mut ty_generics, rgen, is_return, cache, ); } + // The search index, for simplicity's sake, represents fn pointers and closures + // the same way: as a tuple for the parameters, and an associated type for the + // return type. + let mut ty_output = Vec::new(); + simplify_fn_type( + self_, + generics, + &bf.decl.output, + tcx, + recurse + 1, + &mut ty_output, + rgen, + is_return, + cache, + ); + let ty_bindings = vec![(RenderTypeId::AssociatedType(sym::Output), ty_output)]; + res.push(RenderType { + id: get_index_type_id(&arg, rgen), + bindings: Some(ty_bindings), + generics: Some(ty_generics), + }); } - // Every trait associated type on self gets assigned to a type parameter index - // this same one is used later for any appearances of these types - // - // for example, Iterator::next is: - // - // trait Iterator { - // fn next(&mut self) -> Option - // } - // - // Self is technically just Iterator, but we want to pretend it's more like this: - // - // fn next(self: Iterator) -> Option - if is_self - && let Type::Path { path } = arg - && let def_id = path.def_id() - && let Some(trait_) = cache.traits.get(&def_id) - && trait_.items.iter().any(|at| at.is_ty_associated_type()) - { - for assoc_ty in &trait_.items { - if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &*assoc_ty.kind - && let Some(name) = assoc_ty.name - { - let idx = -isize::try_from(rgen.len() + 1).unwrap(); - let (idx, stored_bounds) = rgen - .entry(SimplifiedParam::AssociatedType(def_id, name)) - .or_insert_with(|| (idx, Vec::new())); - let idx = *idx; - if stored_bounds.is_empty() { - // Can't just pass stored_bounds to simplify_fn_type, - // because it also accepts rgen as a parameter. - // Instead, have it fill in this local, then copy it into the map afterward. - let mut type_bounds = Vec::new(); - for bound in bounds { - if let Some(path) = bound.get_trait_path() { - let ty = Type::Path { path }; - simplify_fn_type( - self_, - generics, - &ty, - tcx, - recurse + 1, - &mut type_bounds, - rgen, - is_return, - cache, - ); - } - } - let stored_bounds = &mut rgen - .get_mut(&SimplifiedParam::AssociatedType(def_id, name)) - .unwrap() - .1; + Type::BorrowedRef { lifetime: _, mutability, ref type_ } => { + let mut ty_generics = Vec::new(); + if mutability.is_mut() { + ty_generics.push(RenderType { + id: Some(RenderTypeId::Mut), + generics: None, + bindings: None, + }); + } + simplify_fn_type( + self_, + generics, + &type_, + tcx, + recurse + 1, + &mut ty_generics, + rgen, + is_return, + cache, + ); + res.push(get_index_type(arg, ty_generics, rgen)); + } + _ => { + // This is not a type parameter. So for example if we have `T, U: Option`, and we're + // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't. + // + // So in here, we can add it directly and look for its own type parameters (so for `Option`, + // we will look for them but not for `T`). + let mut ty_generics = Vec::new(); + let mut ty_constraints = Vec::new(); + if let Some(arg_generics) = arg.generic_args() { + for ty in arg_generics.into_iter().filter_map(|param| match param { + clean::GenericArg::Type(ty) => Some(ty), + _ => None, + }) { + simplify_fn_type( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut ty_generics, + rgen, + is_return, + cache, + ); + } + for constraint in arg_generics.constraints() { + simplify_fn_constraint( + self_, + generics, + &constraint, + tcx, + recurse + 1, + &mut ty_constraints, + rgen, + is_return, + cache, + ); + } + } + // Every trait associated type on self gets assigned to a type parameter index + // this same one is used later for any appearances of these types + // + // for example, Iterator::next is: + // + // trait Iterator { + // fn next(&mut self) -> Option + // } + // + // Self is technically just Iterator, but we want to pretend it's more like this: + // + // fn next(self: Iterator) -> Option + if is_self + && let Type::Path { path } = arg + && let def_id = path.def_id() + && let Some(trait_) = cache.traits.get(&def_id) + && trait_.items.iter().any(|at| at.is_ty_associated_type()) + { + for assoc_ty in &trait_.items { + if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &*assoc_ty.kind + && let Some(name) = assoc_ty.name + { + let idx = -isize::try_from(rgen.len() + 1).unwrap(); + let (idx, stored_bounds) = rgen + .entry(SimplifiedParam::AssociatedType(def_id, name)) + .or_insert_with(|| (idx, Vec::new())); + let idx = *idx; if stored_bounds.is_empty() { - *stored_bounds = type_bounds; + // Can't just pass stored_bounds to simplify_fn_type, + // because it also accepts rgen as a parameter. + // Instead, have it fill in this local, then copy it into the map afterward. + let mut type_bounds = Vec::new(); + for bound in bounds { + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + simplify_fn_type( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut type_bounds, + rgen, + is_return, + cache, + ); + } + } + let stored_bounds = &mut rgen + .get_mut(&SimplifiedParam::AssociatedType(def_id, name)) + .unwrap() + .1; + if stored_bounds.is_empty() { + *stored_bounds = type_bounds; + } } + ty_constraints.push(( + RenderTypeId::AssociatedType(name), + vec![RenderType { + id: Some(RenderTypeId::Index(idx)), + generics: None, + bindings: None, + }], + )) } - ty_constraints.push(( - RenderTypeId::AssociatedType(name), - vec![RenderType { - id: Some(RenderTypeId::Index(idx)), - generics: None, - bindings: None, - }], - )) } } - } - let id = get_index_type_id(&arg, rgen); - if id.is_some() || !ty_generics.is_empty() { - res.push(RenderType { - id, - bindings: if ty_constraints.is_empty() { None } else { Some(ty_constraints) }, - generics: if ty_generics.is_empty() { None } else { Some(ty_generics) }, - }); + let id = get_index_type_id(&arg, rgen); + if id.is_some() || !ty_generics.is_empty() { + res.push(RenderType { + id, + bindings: if ty_constraints.is_empty() { None } else { Some(ty_constraints) }, + generics: if ty_generics.is_empty() { None } else { Some(ty_generics) }, + }); + } } } } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f4e231327a8c2..02a6bb8f5487a 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -930,7 +930,7 @@ a.doc-anchor { left: -17px; /* 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; + padding-right: 10px; /* And this padding is used to make the anchor larger and easier to click on. */ padding-left: 3px; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index e0ea234f9e710..62fbde6f225b1 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -390,13 +390,18 @@ function preLoadCss(cssUrl) { if (splitAt !== -1) { const implId = savedHash.slice(0, splitAt); const assocId = savedHash.slice(splitAt + 1); - const implElem = document.getElementById(implId); - if (implElem && implElem.parentElement.tagName === "SUMMARY" && - implElem.parentElement.parentElement.tagName === "DETAILS") { - onEachLazy(implElem.parentElement.parentElement.querySelectorAll( + const implElems = document.querySelectorAll( + `details > summary > section[id^="${implId}"]`, + ); + onEachLazy(implElems, implElem => { + const numbered = /^(.+?)-([0-9]+)$/.exec(implElem.id); + if (implElem.id !== implId && (!numbered || numbered[1] !== implId)) { + return false; + } + return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), item => { - const numbered = /([^-]+)-([0-9]+)/.exec(item.id); + const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { openParentDetails(item); item.scrollIntoView(); @@ -404,10 +409,11 @@ function preLoadCss(cssUrl) { setTimeout(() => { window.location.replace("#" + item.id); }, 0); + return true; } }, ); - } + }); } } } @@ -568,7 +574,6 @@ function preLoadCss(cssUrl) { //block("associatedconstant", "associated-consts", "Associated Constants"); block("foreigntype", "foreign-types", "Foreign Types"); block("keyword", "keywords", "Keywords"); - block("opaque", "opaque-types", "Opaque Types"); block("attr", "attributes", "Attribute Macros"); block("derive", "derives", "Derive Macros"); block("traitalias", "trait-aliases", "Trait Aliases"); @@ -1115,8 +1120,7 @@ function preLoadCss(cssUrl) { wrapper.style.left = 0; wrapper.style.right = "auto"; wrapper.style.visibility = "hidden"; - const body = document.getElementsByTagName("body")[0]; - body.appendChild(wrapper); + document.body.appendChild(wrapper); const wrapperPos = wrapper.getBoundingClientRect(); // offset so that the arrow points at the center of the "(i)" const finalPos = pos.left + window.scrollX - wrapperPos.width + 24; @@ -1235,8 +1239,7 @@ function preLoadCss(cssUrl) { } window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; } - const body = document.getElementsByTagName("body")[0]; - body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); + document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); window.CURRENT_TOOLTIP_ELEMENT = null; } @@ -1833,7 +1836,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm let elem = event.target; while (!hasClass(elem, "example-wrap")) { elem = elem.parentElement; - if (elem.tagName === "body" || hasClass(elem, "docblock")) { + if (elem === document.body || hasClass(elem, "docblock")) { return null; } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index db9dafab9a484..b97d710c007ce 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -333,7 +333,6 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { ForeignStaticItem(s, _) => ItemEnum::Static(s.into_tcx(tcx)), ForeignTypeItem => ItemEnum::ForeignType, TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)), - OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)), // FIXME(generic_const_items): Add support for generic free consts ConstantItem(ci) => { ItemEnum::Constant { type_: ci.type_.into_tcx(tcx), const_: ci.kind.into_tcx(tcx) } @@ -579,7 +578,7 @@ impl FromWithTcx for Type { fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self { use clean::Type::{ Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath, - RawPointer, Slice, Tuple, + RawPointer, SelfTy, Slice, Tuple, }; match ty { @@ -589,6 +588,8 @@ impl FromWithTcx for Type { traits: bounds.into_tcx(tcx), }), Generic(s) => Type::Generic(s.to_string()), + // FIXME: add dedicated variant to json Type? + SelfTy => Type::Generic("Self".to_owned()), Primitive(p) => Type::Primitive(p.as_sym().to_string()), BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))), Tuple(t) => Type::Tuple(t.into_tcx(tcx)), @@ -830,12 +831,6 @@ impl FromWithTcx> for TypeAlias { } } -impl FromWithTcx for OpaqueTy { - fn from_tcx(opaque: clean::OpaqueTy, tcx: TyCtxt<'_>) -> Self { - OpaqueTy { bounds: opaque.bounds.into_tcx(tcx), generics: opaque.generics.into_tcx(tcx) } - } -} - impl FromWithTcx for Static { fn from_tcx(stat: clean::Static, tcx: TyCtxt<'_>) -> Self { Static { @@ -867,7 +862,6 @@ impl FromWithTcx for ItemKind { Enum => ItemKind::Enum, Function | TyMethod | Method => ItemKind::Function, TypeAlias => ItemKind::TypeAlias, - OpaqueTy => ItemKind::OpaqueTy, Static => ItemKind::Static, Constant => ItemKind::Constant, Trait => ItemKind::Trait, diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 033f01864f149..ea191dc89cfde 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -184,7 +184,6 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { | types::ItemEnum::TraitAlias(_) | types::ItemEnum::Impl(_) | types::ItemEnum::TypeAlias(_) - | types::ItemEnum::OpaqueTy(_) | types::ItemEnum::Constant { .. } | types::ItemEnum::Static(_) | types::ItemEnum::ForeignType @@ -217,13 +216,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { fn after_krate(&mut self) -> Result<(), Error> { debug!("Done with crate"); - debug!("Adding Primitive impls"); - for primitive in Rc::clone(&self.cache).primitive_locations.values() { - self.get_impls(*primitive); - } - let e = ExternalCrate { crate_num: LOCAL_CRATE }; - let index = (*self.index).clone().into_inner(); debug!("Constructing Output"); diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index d3368186d3ab8..d1e2b9978f7de 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -50,8 +50,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { return Some(ret); } // These items can all get re-exported - clean::OpaqueTyItem(..) - | clean::TypeAliasItem(..) + clean::TypeAliasItem(..) | clean::StaticItem(..) | clean::StructItem(..) | clean::EnumItem(..) diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index de836439be957..430bbe991eaaf 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -26,7 +26,6 @@ pub(crate) trait DocVisitor: Sized { | ImportItem(_) | FunctionItem(_) | TypeAliasItem(_) - | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) diff --git a/src/llvm-project b/src/llvm-project index c54cff0e6e4d1..57ae1a3474057 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit c54cff0e6e4d1a0d0a2df7c1ce3d96cdd554763e +Subproject commit 57ae1a3474057fead2c438928ed368b3740bf0ec diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 841a87fc48a32..a93ac6ccaf1df 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 32; +pub const FORMAT_VERSION: u32 = 33; /// The root of the emitted JSON blob. /// @@ -326,7 +326,6 @@ pub enum ItemKind { Function, /// A type alias declaration, e.g. `type Pig = std::borrow::Cow<'static, str>;` TypeAlias, - OpaqueTy, /// The declaration of a constant, e.g. `const GREETING: &str = "Hi :3";` Constant, /// A `trait` declaration. @@ -414,7 +413,6 @@ pub enum ItemEnum { /// A type alias declaration, e.g. `type Pig = std::borrow::Cow<'static, str>;` TypeAlias(TypeAlias), - OpaqueTy(OpaqueTy), /// The declaration of a constant, e.g. `const GREETING: &str = "Hi :3";` Constant { /// The type of the constant. @@ -1200,12 +1198,6 @@ pub struct TypeAlias { pub generics: Generics, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct OpaqueTy { - pub bounds: Vec, - pub generics: Generics, -} - /// A `static` declaration. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Static { diff --git a/src/stage0 b/src/stage0 index f36010fa4b459..dcc22673f13ed 100644 --- a/src/stage0 +++ b/src/stage0 @@ -14,434 +14,448 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2024-06-11 +compiler_date=2024-07-26 compiler_version=beta -rustfmt_date=2024-06-11 +rustfmt_date=2024-07-30 rustfmt_version=nightly -dist/2024-06-11/rustc-beta-x86_64-unknown-freebsd.tar.gz=4bf5a3ae2656a992beedec2f41dc98c791426a1a6a88f3503b003b4d6f0ddc1f -dist/2024-06-11/rustc-beta-x86_64-unknown-freebsd.tar.xz=feaa1484586e78d77225734328d460277dde14baaf1299a30b30ef91e9a26a82 -dist/2024-06-11/rustc-beta-x86_64-unknown-illumos.tar.gz=9c692a71916f7ca9d41a97416f51d463b0495311491ba92b4d3cdb0f6c4e7c87 -dist/2024-06-11/rustc-beta-x86_64-unknown-illumos.tar.xz=97dece0cc2e4856a49f9ae17f09171fb55f140987acd0f988bbd0aa1e8971936 -dist/2024-06-11/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=6ad2e87e637302e8458d47dc1af923e38bffd7332775cfa5520c29110594d9b9 -dist/2024-06-11/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=84f6755c1377ced8ed2700b5ca9a2d38dd2e26a03e1fd636841ad46d3cf63895 -dist/2024-06-11/rustc-beta-i686-unknown-linux-gnu.tar.gz=11b439045379be2722cb42409b8bc342ff4b5f7df85e42566006cd72bb1a4e8a -dist/2024-06-11/rustc-beta-i686-unknown-linux-gnu.tar.xz=4e5103702b4e43ea71914e9c508aeb0db6382ea331c31981142163b2a89e19e1 -dist/2024-06-11/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=396a3aaefca9511086f7e5442209b70622082d133b96783b4237c3f01f42bf87 -dist/2024-06-11/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=66e46ed344af9f165b7c4fde2c576340fa81ecade61c45ecc1562de70a9203da -dist/2024-06-11/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=831420b262c5a2f44459e0daceebe271f298179c37aa5b7a1704a15bbbb1fb97 -dist/2024-06-11/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=691a6c4d0276add72cfab40968520ff48f683d4e660a50d1626ba9564dbf4914 -dist/2024-06-11/rustc-beta-aarch64-unknown-linux-musl.tar.gz=ebe1efe3c5d74e37d1c40dbd585ada7a7e256ce76911746f54994dfa15f9907f -dist/2024-06-11/rustc-beta-aarch64-unknown-linux-musl.tar.xz=3bb37fcab069713fe8aa5ca72291a439eba5af4a3cc0be9d152e5f29ccaef864 -dist/2024-06-11/rustc-beta-s390x-unknown-linux-gnu.tar.gz=c00c2ecb5aae552069395d03f4135f051da073efc68ed645348f026c1884799b -dist/2024-06-11/rustc-beta-s390x-unknown-linux-gnu.tar.xz=2c72347688058091a4bcb0c2fbcff49020df2b2c1733bc19d93b6e5149d5b13a -dist/2024-06-11/rustc-beta-aarch64-pc-windows-msvc.tar.gz=517977edaa51470d460aa9ec09309eea2082cffa6adb13571a8f36efa9351dff -dist/2024-06-11/rustc-beta-aarch64-pc-windows-msvc.tar.xz=0cc0b740cb267fde814b4971591b5797faa528b1e6250ef7b2d5220f0916208d -dist/2024-06-11/rustc-beta-x86_64-pc-windows-gnu.tar.gz=9fe7d85fdbca12fbfdb776789c336f726303c8845a6f6c93b2dc6ea8487585d4 -dist/2024-06-11/rustc-beta-x86_64-pc-windows-gnu.tar.xz=38130e3d9779ad54bd890c4001b34b7c58368ed1940e7e676947e9c05a8f6649 -dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=acca3e045de3c0d908caec4249e3905d207d96b4a1722644457b3cbe5707422c -dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=e4e92fc4b672f72b1f03db62ae712d9f944b982db89cf0d8dcb94adb1d560b3e -dist/2024-06-11/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=63274e707bd0d2b2f08c49f170f26773f802db833347d2aa694d345d11c841f5 -dist/2024-06-11/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=13702b24228d983710d5678e0a1003e4f77e2244fbdf599015f52adbbb77bfcc -dist/2024-06-11/rustc-beta-x86_64-pc-windows-msvc.tar.gz=ca877491bf7bf50989b9ac21c12e335fd0ff002ccf4a2c07316ae1df1a3d7e6b -dist/2024-06-11/rustc-beta-x86_64-pc-windows-msvc.tar.xz=d757a328595887039d8017e9e3faf79448850c3dd9bd3c26d309a313ccc09a38 -dist/2024-06-11/rustc-beta-x86_64-unknown-linux-musl.tar.gz=155497f2abe036f09f6aa014969d58132a9e2e4e148fea485b483e0dc3705a39 -dist/2024-06-11/rustc-beta-x86_64-unknown-linux-musl.tar.xz=6204d1fa08beeadcf1564e334cbb65a7317c571557cbc5517eb6bf08f5b0ce75 -dist/2024-06-11/rustc-beta-aarch64-apple-darwin.tar.gz=3bd7db536344c6a3d0d0bdf95986136d5db8996228802444b561783f2e7c3c9c -dist/2024-06-11/rustc-beta-aarch64-apple-darwin.tar.xz=b425561b3e7d9130fae94e78522bb262c634304ced32030828743fb332b11ecb -dist/2024-06-11/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=e2f78198d33f98aa34bedd4f5920868a8f68620e29eca2917833f6a8f23f787c -dist/2024-06-11/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=22f3b45678081fe52a9c8e89234a9939aad5623eb43ec54921effc28c0bdcd80 -dist/2024-06-11/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=beb5ec95069cbac4e53c51ea3e785ab5f25122132c8b78b196372bb2a31ed46f -dist/2024-06-11/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=17242f74c96cf581f2e40a82f6d7fbd585af2c2990cc73ad4ba7a3503dd279a3 -dist/2024-06-11/rustc-beta-x86_64-apple-darwin.tar.gz=d952d90397c70c5b53caa98b2e5d48e35780e1e5b342775c10fcbf504717d2ec -dist/2024-06-11/rustc-beta-x86_64-apple-darwin.tar.xz=14bd4336eb396a133ddbb2acd01ae799f2f68a7ac723a00a9db8c3e06bfa8027 -dist/2024-06-11/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=a2118f80091d62ebb762df0bdd159d19befb440d754034da7348aae50d63628f -dist/2024-06-11/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=a4b9d1a04d8f9cfd4daaf57fbeed3615d000c5107c6fa1b2992dad6ca552e35a -dist/2024-06-11/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=d5c16c5621e43cd3007ba4d2bc0f6e8a3a2376bc2a911e42bd2f2670ff94c12c -dist/2024-06-11/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=202662492afd863b6b6428a3bc5ed822c62ac2aeb8a0264df1d69cc5a3728d8f -dist/2024-06-11/rustc-beta-i686-pc-windows-msvc.tar.gz=a9fb64cac53595444bdbe8f152e52214812498e0c6e2b664db10f1f693a92dca -dist/2024-06-11/rustc-beta-i686-pc-windows-msvc.tar.xz=fe78c5ac51d96c3c3e222828dca3d105a2d4d7f8acd6e0d86ecd4b27ea70a4c3 -dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=8c584010c964ef1b02e1014ca1c6ed9584c1ff781f8af115ee001e3a13213954 -dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=4a407fb59db284ddf6fc0ccd487116fafbcd7c227f3dfd4e0b7b64dd560dc113 -dist/2024-06-11/rustc-beta-x86_64-unknown-netbsd.tar.gz=2f07a283bb0e57ad50366b67d216503a423867eae205e4354b74b748e9d3ce85 -dist/2024-06-11/rustc-beta-x86_64-unknown-netbsd.tar.xz=8008331b32e9d0daeb275327082e38671dcc91864a3ac95be67bd15ecaa852ad -dist/2024-06-11/rustc-beta-i686-pc-windows-gnu.tar.gz=c42637a8aa8666d746a45c9f47b437a9720d355b5bc6db42c2a90fb03d2c4a26 -dist/2024-06-11/rustc-beta-i686-pc-windows-gnu.tar.xz=eabe3a7e72829e0e5904ef381157cc3da169d9f5942e0367b2d1b1781c387560 -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=f4010eea815cbab895c96311a456c4e3aa24c5f0d55f09052bcde59de7f286e6 -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=a8f555c713cbe4a1860ee5bc812eb624f2775e034da54bd0e37c325453d70448 -dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=c89619ed4ec28d4f48ef2f7e51155716483a7f4bb51fe38235e69d4694cfcbe5 -dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=89aa3d1add14298bd40e3bcbc6b41df91312258ac6f203fff9eca4422f0c5e9f -dist/2024-06-11/rust-std-beta-x86_64-apple-darwin.tar.gz=2303e7926bcc4fa2c67c7063fcac3a7b581fdd91fb89123cb6cdf514ff38afed -dist/2024-06-11/rust-std-beta-x86_64-apple-darwin.tar.xz=9ddf392d8c696e902c2924ff938a4bd099b9a54834dc62710e42527e464e776f -dist/2024-06-11/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=6016b465a0cfc628f2c978ed18bc19a4a27e20a5ac11a170c40d01a857a5f50a -dist/2024-06-11/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=8e3d6f7ef6efead7d08b1c889bcc6e520d0a6d29226ade1b150aeb625fdb9abd -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=5f53b7630fa7703fc6752c3fb8c8eba8e25b9f77a3b91e14982510a0091d9e44 -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=8ed3c2f0b0f81d197d0dbd01aa777eb17ebc7303176601be2b2b05ebe1bcc971 -dist/2024-06-11/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=dbc1a1bb8cfb4472739d7f3a52bcd571357d2d2761f3723aa4c7d69abe9a5dfc -dist/2024-06-11/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=58f46f20d58669d497ff683d6ea85e18ff68d51a442f6e21bc20ddee779e1c4f -dist/2024-06-11/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=d71947b5ddcfe538162b14d6b785c365c34f2b6ac2f1abdc90b020c4a446a313 -dist/2024-06-11/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=ca0a665e566b3ff74c5f552e7e4e030d3cae9080dbb6055dce3aa41206b5b111 -dist/2024-06-11/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=e927c0890d2c5135d86b1960b6b182d5c47853b53eeb196b4748e7b8ec9362e4 -dist/2024-06-11/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=df33672f826ce8681d6c18ec2129ef47623b7cbaf6515cd921d39dfc05cb7a06 -dist/2024-06-11/rust-std-beta-i686-linux-android.tar.gz=2559fdcf250e7e0a1b5589e8f2bc69340f5610b7e6ddf08185936fa2e6f10534 -dist/2024-06-11/rust-std-beta-i686-linux-android.tar.xz=442f0aa53f343eec2b6984934bbf0651ff45f314d1f46dad2aa11a2f5c5a5a5b -dist/2024-06-11/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=cb657852d05766e1c18b11d99a94f3570b6c6147dbf9b2113b80236566d2d4ac -dist/2024-06-11/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=943de72c556821b67d59afd9dd128bfb01b3ba743dd3e7561c50559afd3768f4 -dist/2024-06-11/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=a7c6d3f54d925be073007e3737173b0def7d781b6bfd5345971a69300aeee9c3 -dist/2024-06-11/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=cb9115328bf6f5ae661ce5bc9cc9165c6c9e6f759f3cc5038154531b314e6013 -dist/2024-06-11/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=2a2dac9fba84958818f6b31f30f91fcfef521111827937c88aa696b646f5b075 -dist/2024-06-11/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=d16b28da0196a815f028d432dfa0957d0bfb512b259129969548bd050afac872 -dist/2024-06-11/rust-std-beta-armv7a-none-eabi.tar.gz=d78f96102490f9025598b11cb72e31e96d3f5fd566e8f7b2b2c503a37b0dc6c3 -dist/2024-06-11/rust-std-beta-armv7a-none-eabi.tar.xz=a439af58e90970b4dadaa04d8f94ea53c6c7a159117926c46b7da0eaa0d0ca5b -dist/2024-06-11/rust-std-beta-i686-pc-windows-msvc.tar.gz=d7847f3d9b1a2f50541ef3d2c25eeda469b28412817cadca52756aeb4f2c57b1 -dist/2024-06-11/rust-std-beta-i686-pc-windows-msvc.tar.xz=622cafc347ec1d3f0ef70354a0713cd255029730b17ad1f1ee951d9b0cb515e5 -dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=5fee2542ee8ee8d155edc0ac36ec8822896e6f93f9f573b9284d70aeb186a05d -dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=dfe9ca3e3f217922b65db0d65826c0962369fefd3d5d72d6f03039acd710f122 -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=b0937c5dcd458f313aabe07668ea338f2629bfc9c45d6139211b6923a7c7eb0e -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=65973a26c69f8ca7d3022180e2729161f7a618706cd9974fc54b14cf48f1af79 -dist/2024-06-11/rust-std-beta-aarch64-unknown-none.tar.gz=def2fc3aca20a74d023156606067f88ea8f1c8b602fb628b8f27ee43e3b62cfd -dist/2024-06-11/rust-std-beta-aarch64-unknown-none.tar.xz=43cdb54af35a6c00ee10759a3f6ff6ca5c9080ba817aaf2eab7df8f239d77196 -dist/2024-06-11/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=ae2cbe53d304442e112585dd968f72454cc33bb75d3b4d7854a04575ff7b5301 -dist/2024-06-11/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=b3192290a45ff978126f4f08e7ddaf6de8720a2f6abbc5b41536fad6f994d7ae -dist/2024-06-11/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=0e2dc167dbb66366252100fc87b94a67cca3360343302697c2989beb2eb14704 -dist/2024-06-11/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=6144604269f533eed11471b4c5d2f31a9f8340d9edf0f398a42e6e970c95b991 -dist/2024-06-11/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=b57e11248924ac54500d80525b8fe823e7dc74bc579c2b7f3f076c0a4c85ee2a -dist/2024-06-11/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=9e473a5de0d02e6d8278a9e2430af135e3d6d4ecf5ce59827b384c8f20af39c2 -dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=1a62b5af52767e68146a45a47384b33c93b32a4579e51d41f66746e38dfa6eba -dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=81bcf448ec54470edf60c2079ad9ef03f34052aef5fe9d8f5af933c565fdfd29 -dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=88eb49f86265a7fdb49b455cf5a9a373ac1e0f7cbcac08ef2194eaefbb039b22 -dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=bf9a278615765e199786d16f154006007ddfca0d5a5869c55a05e513a7b06580 -dist/2024-06-11/rust-std-beta-thumbv7em-none-eabihf.tar.gz=13f16402dea66facba6e10a9556e0afdbd6b004e8aa5d08e106cf76ea59b8288 -dist/2024-06-11/rust-std-beta-thumbv7em-none-eabihf.tar.xz=7ecd66dc65758cdf32cdebc38f6f399392788a7085d69ec7e8fbd63be7b3e904 -dist/2024-06-11/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=96ccb2252defcd5bcd9eca19380b9d4e4115ffb135efebe6717b1527ee440bf8 -dist/2024-06-11/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=c31f4ef95857a445bc480b73babb886e259443b3137efd694c4b67f913838212 -dist/2024-06-11/rust-std-beta-thumbv7em-none-eabi.tar.gz=0637fe1ab1284334ed4f9aec87a4b51b6983418997844e10ffe20326078f6d18 -dist/2024-06-11/rust-std-beta-thumbv7em-none-eabi.tar.xz=ac7f13ba1a107d03c1b7bb0d2e6753b926a26554c82de38263113e2d5dfeca21 -dist/2024-06-11/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=0e8a21970d4ea6331968b10e990cb6dbe3d38af393e4dc8d603efec1b59aa0bd -dist/2024-06-11/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=04b9663cef2d3507c96b77b18aff7c9beee0f6bd0e0cb9ef37b8c9cacadbb198 -dist/2024-06-11/rust-std-beta-wasm32-unknown-emscripten.tar.gz=01d4da0efebf4615da7c92c6cfdaedaf283d3f5e66c58a1b1e2e17488d86b3d3 -dist/2024-06-11/rust-std-beta-wasm32-unknown-emscripten.tar.xz=c203ec9216407f90d0bcc451e40a29e50279dae8ee161f05d9245e3cc8e1b30a -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=3621e0a16f7e6d9f292894a343941399a54b5d76a858fe5a69b83c95f15ada07 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=5944208b88c6924f8586304abea6827cdf756066158dc64200e207027143e843 -dist/2024-06-11/rust-std-beta-aarch64-unknown-uefi.tar.gz=1887e342d5c6453a63336023e51c124838762f89e9289d1abd8226a717bb96d3 -dist/2024-06-11/rust-std-beta-aarch64-unknown-uefi.tar.xz=ba0ca831ed66a72a14c8c9dd591e97d7d6cbc55762c961283088e897de4a8669 -dist/2024-06-11/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=59790fb6982ebfefc40d352d411604840b7540a6ddfb26ad14d70e139df3ab99 -dist/2024-06-11/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=c022c80da2debecdbea32ce34ef040bdebc8566a78e2867512815532746e1c95 -dist/2024-06-11/rust-std-beta-armebv7r-none-eabihf.tar.gz=4d4a380bed233e86ea957c173412b819fce00d66d369657a0778da5e4f244034 -dist/2024-06-11/rust-std-beta-armebv7r-none-eabihf.tar.xz=577763a4758c42bcf0c875d606a6dd538c07866e9a65c2b73271f0e9d0762830 -dist/2024-06-11/rust-std-beta-i586-unknown-linux-gnu.tar.gz=29f8e4dc5d88bfd66f600402022267ad85d4afcffd7142107d0fd28d0cae4cd8 -dist/2024-06-11/rust-std-beta-i586-unknown-linux-gnu.tar.xz=d030db59dd1788fa8438c52b9a8cbc6754eaf529de60fa4fa9693a687e644dfb -dist/2024-06-11/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=df978c1519ea4b50fcec2ec18d2cd9a6b813982dc50f6442af9ce340b98a8c46 -dist/2024-06-11/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=13585b56ce162922dbf03d0b6432a6af9b9059a664063a6adca957301c23b905 -dist/2024-06-11/rust-std-beta-wasm32-unknown-unknown.tar.gz=6365c96d363a345c4882ad47a3e9242f37c930a10bdc89d137e5e05b1b599351 -dist/2024-06-11/rust-std-beta-wasm32-unknown-unknown.tar.xz=480e07d3f110ff9f201315f5a01bf674ead911773241d80d9005772cfe4a3d88 -dist/2024-06-11/rust-std-beta-armv7r-none-eabi.tar.gz=0514e7022540078895be3000fa0405157bf1165762e77672f78230e62ef2c8ec -dist/2024-06-11/rust-std-beta-armv7r-none-eabi.tar.xz=659cec4d3e3f0783bf9a8b42a245b995e00b36a8ff04a9b6b458ace016d825d1 -dist/2024-06-11/rust-std-beta-i686-pc-windows-gnu.tar.gz=2c6b17e0fa22a9d528217fdb8231aefd21839e29babad0fd975fd3654a9706f2 -dist/2024-06-11/rust-std-beta-i686-pc-windows-gnu.tar.xz=c2c98b0c6b21618d5c49283f19ccdd3ba76159afd904c9683251d36317582615 -dist/2024-06-11/rust-std-beta-x86_64-pc-solaris.tar.gz=9433aed1f5f87157cf02d13e7ad375e9a2e9a260afb8419a05eeb1856069ab26 -dist/2024-06-11/rust-std-beta-x86_64-pc-solaris.tar.xz=2a39a792676fd60f47db90979fd56f3fee709d05d91b5c8bf4e0a01f39fed14e -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=29e4553e9889c3e202f550042555b5c24cf52ce68979870fe33d9e9eaad37db0 -dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=7d56ef2eb64550f95b2ec1e1cfb9f153e9c6b412dcceef35f76db44eccd4d8a1 -dist/2024-06-11/rust-std-beta-i586-pc-windows-msvc.tar.gz=da7c08348c6834793263e136747e0e3dfcd5cd805c0c6ca25323fab8a30de427 -dist/2024-06-11/rust-std-beta-i586-pc-windows-msvc.tar.xz=3d60ea1671800cb8f271f1869186ed099f35ee8171d23df7c6ef0c79b4567540 -dist/2024-06-11/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=f3cfa8625f3586ddf48c301b7016d49d216324daaa3b76168a97f15506346c3a -dist/2024-06-11/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=2e5388097f10a3640514d1a4040f6fec090e1c4aba0259cbdd900ead7fcdfd94 -dist/2024-06-11/rust-std-beta-i686-unknown-freebsd.tar.gz=cf38eeced5efe6139043e123b217a5c3f43bef3d86001a9a51fa21b012c9b468 -dist/2024-06-11/rust-std-beta-i686-unknown-freebsd.tar.xz=e078625da24308d78603400e96454e60606bed23f1132b64efbbbc815791d58c -dist/2024-06-11/rust-std-beta-aarch64-apple-ios.tar.gz=2f1dcaa67df6adacbb850bf17ed5df8258fd2087668d028814874fc8292bf420 -dist/2024-06-11/rust-std-beta-aarch64-apple-ios.tar.xz=2a11e0abb8b162a12f72825cac59cc0df993b5582d911bdf6b97252078887651 -dist/2024-06-11/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=d7f4366df7f3599f031b9654d3c0bebd9c21ec530330283e6560cceccc17365b -dist/2024-06-11/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=cb9e8470fd9934870cbb881f7611a0bd614416faa42cfa6096a1ed19abdf9cac -dist/2024-06-11/rust-std-beta-wasm32-wasi.tar.gz=5e7d7b535a7c1449a4dcac4022de279c7676852a7da3e63dc063595dd3fa1b54 -dist/2024-06-11/rust-std-beta-wasm32-wasi.tar.xz=c4db5ad4aa261a01ba3fd8a191c7ac6323e6134b2e17522b3fe0891a642d4c87 -dist/2024-06-11/rust-std-beta-x86_64-apple-ios.tar.gz=a27f3ff22b233c72673e32bf257f766ad8e11abfaf9f83fc7c3e756f785ee83e -dist/2024-06-11/rust-std-beta-x86_64-apple-ios.tar.xz=8592a2f8b1d6c3cab199cabe8b6e9a668b90bd77bf6a5e0869e2b4f3cc6d1170 -dist/2024-06-11/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=ffd9d5acd9a2a33e6a699b850d1776521da443b52504ba77fc71d6dd7353e18d -dist/2024-06-11/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=e92eec9cd55b37f135237c98cfe00c4ee23d2ef400cc3470deb8f1a0010e94c3 -dist/2024-06-11/rust-std-beta-x86_64-unknown-illumos.tar.gz=b1d2e427120295375eecc3d8fc647fe57e1195210d7d0243b20d156f173c3155 -dist/2024-06-11/rust-std-beta-x86_64-unknown-illumos.tar.xz=b97fe83bb5050460465c57c37456868368d0ce18e5134fedad07ae00df8b7432 -dist/2024-06-11/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=25ad2d1a91456394427a5db691ebd903e56e60daa89d73598155655caf6592b6 -dist/2024-06-11/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=88b0a7fb002922f78af0f8e9580acdfeb4c341c9fad35e53daf4d6073c73ab8f -dist/2024-06-11/rust-std-beta-x86_64-unknown-netbsd.tar.gz=b5e129c655bdc051eaa5aca8c8a577914100137a5bca753df619edd22889a1c2 -dist/2024-06-11/rust-std-beta-x86_64-unknown-netbsd.tar.xz=5fc42453c00c3498af2fd60fa4345c2047f302113666bf4a1d044539e3c1e66d -dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=afb1e9e37af72ba42b71f2a8a78ef7ba8593dbf8e35501e20a81e2aae204722e -dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=3b7f63b544a5e8aad8a18097b915e01c1fb880d48514bd1fefe3a5d4872eae28 -dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=bc151debda651b76573e77c489c3b9c2be4af0f632e0061e067889f21ab254fb -dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=f2f9225460e2e7fa7d28cee2dfdab483698c6f51b0eeafe49e2d8e44381a727c -dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=5418a327d270c603ddc903ae6061fcb6bc9bca14593f13fe10ab3fe0f4a656ca -dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=423e7f6fd6555c6d850e7317a8677d4778bc1270d3cfefc05614ef1acc61fd96 -dist/2024-06-11/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=d30d36a72c1e874a7f9df7f805f5cea3617f912b5ad5e7163616b07022e0f7ae -dist/2024-06-11/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=4afd0b4ab3787dcbb2b2f686d56844c6aa3af69fbd72d04bd57901fdd58b6333 -dist/2024-06-11/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=0a9b8bd1d39d346a20803ede808815dbf0cd55d9167561c8aabe7db3300a3dda -dist/2024-06-11/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=e36372611dd3cdabd1f32fb942c79bd31b5fc13946448cbf9a178ad5005dc7b5 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=2fe6221704614b167d837f52cfd439d9586e27ae82a34577659f86382c3192fe -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=55ffdc97b8b6d309b1aa8443b49a5fd6133f5d7653069eb7f32625c1c0bf86ea -dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=87386371b64d4d922dc2fd01a0db28191748002b74a59437ddbab572b0d6fce5 -dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=6c1f9a6683a9d3ad8203b9d08ed829e4990675cb07fdc2214fc1f7970dee7e60 -dist/2024-06-11/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=525a64f28aa0d757e82558737db3150ff90ecb885c3da8e0215370e47fb2ff23 -dist/2024-06-11/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=af49d1730949036a48c05a147a3c67885326df5bea314fef2828e04433f33ef0 -dist/2024-06-11/rust-std-beta-armebv7r-none-eabi.tar.gz=eba00bc621ee0235444698e5bc67780bc93765e70bed11634a8555d8d645635b -dist/2024-06-11/rust-std-beta-armebv7r-none-eabi.tar.xz=236fe5262d76fd8ab7c12ab6c231608c7e704e5fc0d95210b47e0c86201d8a8d -dist/2024-06-11/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=ac98cc34543e6faa0429cb9756d1b0d30dc13d8d8a44380b5feecc0e7d083e40 -dist/2024-06-11/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=ed8fffbe7899a7c35e5985adae1b35b7eabd076970a8eac280e834b7877f272c -dist/2024-06-11/rust-std-beta-x86_64-linux-android.tar.gz=fa53ef5ea66f93f9fa46ac84d3cf6a16743b46a941910b092c9c71208a15defd -dist/2024-06-11/rust-std-beta-x86_64-linux-android.tar.xz=66b394876549c4faaf872d4103ee49ef59cfba5c78777c01ad1df00dc6babd14 -dist/2024-06-11/rust-std-beta-sparcv9-sun-solaris.tar.gz=63794d1cc16a541cc07b56870d86db2212c2c8e3f06e11fd9bdca90fd93aa56f -dist/2024-06-11/rust-std-beta-sparcv9-sun-solaris.tar.xz=4d419a3c4402c303b9437c7564e2bea79575623eaa0b300c2fe688d9d8f9db95 -dist/2024-06-11/rust-std-beta-aarch64-linux-android.tar.gz=b82186abf1bb250753a2640def13aa477b606e0601c5c1fdb25af0e37ea638b0 -dist/2024-06-11/rust-std-beta-aarch64-linux-android.tar.xz=57065c7b4f1f5d51d1366164b62b4114ab95f1a68cf551d12db8e9a2dad044dd -dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=bef7ed6391f906226a20f8d2d842fbc335891d7fb92f91b1c5873ce778d70675 -dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=d80843f5f25923605681541b285ebf6868c0368841efacd376aac4dc9f01d0ce -dist/2024-06-11/rust-std-beta-armv7r-none-eabihf.tar.gz=5b7d75f5f9294327ffb3c441b2e99fbc8402d1dfe0afaa20b2981a987e96801b -dist/2024-06-11/rust-std-beta-armv7r-none-eabihf.tar.xz=ab56542b3be354d6c4f19fefe90af2daf59507e969a4144b6553a900e37e2a4e -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=7365d0ccde4cd000b44992c80cf0e48ead99c2a00e18cde4446d9401792606a3 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=69124687ee3ff876f557b7b21c8f78f7015c5f3b6537b65480613469772e00ec -dist/2024-06-11/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=d1307c1b3fab3dcb6b1e5cfd5c30e1ef1c7b7e1e831ccecb00ce35bc2c1d8b9c -dist/2024-06-11/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=8685907906feb9e16e34d4ea72e3ad338df9d6407dca6da81739b100926efcd2 -dist/2024-06-11/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=d8c1c3d800eaedcf23c25f436e30892df1938a618200f0f041fc1921a5b517ac -dist/2024-06-11/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=53493d039721d5a2f54eb49ba7e16a205dd846bee559519182ca0af30111d182 -dist/2024-06-11/rust-std-beta-x86_64-unknown-redox.tar.gz=3f9842f81f45cbf35fd923ea69f1c88696070edfec0dcecac286519820741078 -dist/2024-06-11/rust-std-beta-x86_64-unknown-redox.tar.xz=73bc6b9b72bf6a9ba7dabe63b7a7d426eb47716466000d05802ef78ccb9128e4 -dist/2024-06-11/rust-std-beta-i686-unknown-uefi.tar.gz=c9954b477c329c3706891f77e9163d98aeadd96b1aa8a71f1eac20f316a9269f -dist/2024-06-11/rust-std-beta-i686-unknown-uefi.tar.xz=88876cb6cf03707065fe172540e82faaf5dd55332c939891253476d4da3f8a00 -dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=e731faa24568e97821c5960229dd9a489ea7ead964a5aa052a6db322bcc55c18 -dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=47b529a6e19c9d609c41809fd62906c34f23a5c515bf62d91e88a87cf7cc0c85 -dist/2024-06-11/rust-std-beta-loongarch64-unknown-none.tar.gz=c6150b3b7481f2396b305188a16c4ce2206c440e9d7639446c76566d00dc5159 -dist/2024-06-11/rust-std-beta-loongarch64-unknown-none.tar.xz=e641747ef4ae46e4d0a8a3050d00b1b8c21c0f25da53a54de8e540a9953468db -dist/2024-06-11/rust-std-beta-wasm32-wasip1-threads.tar.gz=37d141ba5beeaefd08834154f66219df734c379c9315c094433aebb75b82bca7 -dist/2024-06-11/rust-std-beta-wasm32-wasip1-threads.tar.xz=2045a0b765de96eb17e18d8028ed2e553cc998fb71a4811e3d848db320f6b9a3 -dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=82de12e6a2b01c0b98c83b4264a4e00ceefc424f673c97826bb28729c3f31382 -dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=c97b8a57ed8eefd60bbfe2df54121c548d9e271d9691def843185bac9d82b703 -dist/2024-06-11/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=6903175fa7cc3fdda5545ce11f4cc504e4183e56c626e25ff2b0d20a4f5190c4 -dist/2024-06-11/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=df06eee91ba57ff2c0a779da5d746db02f682514b8c03de875f03c96d05188e5 -dist/2024-06-11/rust-std-beta-thumbv7m-none-eabi.tar.gz=d7023edb8c723803e114e5b8ed316a6839e0c37d92cf6a2a09b767ee5ea2b7b9 -dist/2024-06-11/rust-std-beta-thumbv7m-none-eabi.tar.xz=41e19659bc0e0445ebafc49740579f0ac23a9af8e233c529ba2132f3d111faa9 -dist/2024-06-11/rust-std-beta-arm-linux-androideabi.tar.gz=42c0a6bfed8ca53b8833dbee78f2eab92bee061c42a4d629e870c8e655bc3728 -dist/2024-06-11/rust-std-beta-arm-linux-androideabi.tar.xz=5f85c99315a18bfc7fcc3f0caa7218ddc3b6548eb04be0ded835b5c45ec2ae89 -dist/2024-06-11/rust-std-beta-wasm32-wasip1.tar.gz=97abd2b9a68973263da0116eb5a0b9ead2145c1550f3190d02b35606bb9dcf58 -dist/2024-06-11/rust-std-beta-wasm32-wasip1.tar.xz=4ba0ef9b136761e739ab8ef125ffd993ccf71161d69e7640d94de6d5df601dff -dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=886562504ee3e73ceedce6cfcc8d128f22a80f0a33c84b9c29920d7760a8a208 -dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=a8f28e60aa1938cdc7b7d46a1fa770d587be9534a291f5367a80a52772a0b458 -dist/2024-06-11/rust-std-beta-x86_64-unknown-none.tar.gz=642e7fbeaa76403ccfaf6c3c1bfaa8b94ded7311abfece44c863df231c282406 -dist/2024-06-11/rust-std-beta-x86_64-unknown-none.tar.xz=b1441ef88a1a97ecb6d7b674b571beeefab9f59fc8799d9e63b8a01078042f15 -dist/2024-06-11/rust-std-beta-i686-unknown-linux-gnu.tar.gz=fba54c97da35d0f11af99dca7cb0f43784da2104c461518149974da1f2248301 -dist/2024-06-11/rust-std-beta-i686-unknown-linux-gnu.tar.xz=e7acfab1f7bb8b2c2a0f7558b813f627a3f6b6dadcd05f62c5895f753c2f3ffa -dist/2024-06-11/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=e4d5ec39520d069609f32c4dc7dd4114623bcd112a729529a096c992fc19f61e -dist/2024-06-11/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=94d508cc6c86516ae5e3bba9366cb466635a1e3f41fd00ba6b1a44a87175fea5 -dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=76e17967c09311f9e0c684531f21588d5bfbbdd4c707e454068df240fcfa56ab -dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=e627a81855b3d93497b071138917dcf6382b60793a4a76073769537531d01789 -dist/2024-06-11/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=aa5a1b430e31bd5f912d378eab8447ecd920a5840c03b01799a410d86a7c4850 -dist/2024-06-11/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=20f6fc50caaff58aa2883696c61031e9cec5cc24aea94026cf9b84a7642c4d31 -dist/2024-06-11/rust-std-beta-i686-unknown-linux-musl.tar.gz=62d2daf4c1a0a6b4630559c64bc22ceccd658ea0e9e74ebf58a9b791226964fd -dist/2024-06-11/rust-std-beta-i686-unknown-linux-musl.tar.xz=34681e4cac9a6e18f1952f51d6192c8b71b970a0edaf0a06766e1a576a7898d9 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=da45a5c9a7c7c1db32d6664358699ceeb27f59247862190a93ac203e5f49f116 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=f881c61a8ed04f02576374a82f28505be2ad4b694254503d30c1ab415b6b5a74 -dist/2024-06-11/rust-std-beta-i586-unknown-linux-musl.tar.gz=2056b8d9d54baeab1fcf7dcf65916645f8404413bff60851fd97108b7822a54b -dist/2024-06-11/rust-std-beta-i586-unknown-linux-musl.tar.xz=d06fd5dcb976c5331185d308e2ac0e53597275a57603aa171a753b3d005f0667 -dist/2024-06-11/rust-std-beta-armv7-linux-androideabi.tar.gz=27fb1ba34b2331810174f88d0e75075d6af26ef3fb7cd3ec820408e819f36cc8 -dist/2024-06-11/rust-std-beta-armv7-linux-androideabi.tar.xz=f44ff932cecc16cb292a6149ac42fb440b5e4a8a6b56f55634d9031002eb56f7 -dist/2024-06-11/rust-std-beta-aarch64-apple-darwin.tar.gz=3ab3178d3c1d4b9908dc2d0b6236c147e8f787f323c0c02fb924e2d692ec839e -dist/2024-06-11/rust-std-beta-aarch64-apple-darwin.tar.xz=4b67844f0413cd1b0fde9702542eb2ce297020c29531230c37d3e2f65d56b0e3 -dist/2024-06-11/rust-std-beta-aarch64-apple-ios-sim.tar.gz=317035259b48845882a4561bd1c61108331ce68231b3362fa6e9b0a323ea3fc5 -dist/2024-06-11/rust-std-beta-aarch64-apple-ios-sim.tar.xz=04585ea6b3e53cdf62372dc64f00237d26991a2f7ca6ce06f921065957c2ffc8 -dist/2024-06-11/rust-std-beta-x86_64-unknown-freebsd.tar.gz=048684f3b437110dee704d8ef58f166806d69504a0fbf661ad71d19575cc8436 -dist/2024-06-11/rust-std-beta-x86_64-unknown-freebsd.tar.xz=43762fd9d2cf4dda5e8b64dccbabf5637dde37685530eceba060faeba8212b62 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=40b3feff4834beb31125cffff479288adb2cc8a2d9b8963943907ba2b5478589 -dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=f088ad7f9697570c251655adbf6921341f74bb8deaa445cdb0d15e5ab556699c -dist/2024-06-11/rust-std-beta-thumbv6m-none-eabi.tar.gz=94b65456fa327c6d8acd59f1fd004feea8572374e8eadc022338de75c45a6c03 -dist/2024-06-11/rust-std-beta-thumbv6m-none-eabi.tar.xz=9ca368829aaab88a93abb580a0ecc1195ce5fa3877bd556c856ab52af51095ec -dist/2024-06-11/rust-std-beta-x86_64-unknown-uefi.tar.gz=1a9892b1efce5e6aaa361675e008c623c47056a64deb73246c4211f5194f31a9 -dist/2024-06-11/rust-std-beta-x86_64-unknown-uefi.tar.xz=e9a2df6f754ff4c1ced354250de51939efb1e003843f1d0d8962c2a25e7c0f46 -dist/2024-06-11/cargo-beta-x86_64-unknown-illumos.tar.gz=d6f794f092c47e97e822dd6526ac2622f8a7b3a3cfa8b8462b3378eb16dc4801 -dist/2024-06-11/cargo-beta-x86_64-unknown-illumos.tar.xz=bcf8204f4750c6ea9ff5667ae07de7d06e5b68e78176adb79fc1bc433f7d90c0 -dist/2024-06-11/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=27ba1abe717de9e96b802c8026a17a6b5f5dc728ba4813c705a03d54c234be3f -dist/2024-06-11/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=55cbb9abf863c05f13049bc59e99ea3e64523f0193ba1dd155b21cb732db50c2 -dist/2024-06-11/cargo-beta-i686-pc-windows-msvc.tar.gz=ceb0b610395fb2519bc6207b7916f402a17fac9685cf8b5c2ee354b350b396ff -dist/2024-06-11/cargo-beta-i686-pc-windows-msvc.tar.xz=73f1cca9028510331477a0299e214d50a3b9ad02ac035d056acce110f52a1463 -dist/2024-06-11/cargo-beta-s390x-unknown-linux-gnu.tar.gz=eb0dbf227a1ad39436af2334ddb6e90831881c75c24855e9c040bcbb6ce8d876 -dist/2024-06-11/cargo-beta-s390x-unknown-linux-gnu.tar.xz=1b95d58699e8f534b5d93ad02c4281d8e791e8c266f10820de887fe13b1d88c6 -dist/2024-06-11/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=b078cbb4e2e18b6da3618a1048b038c52bf37a398618179b1c03f25d5e154ce0 -dist/2024-06-11/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=80bd1e79864e21ed914a6a2b8ee5d66867734252002789c6d76c3f7e82133eab -dist/2024-06-11/cargo-beta-x86_64-unknown-netbsd.tar.gz=e2fb5140e90588e6ab158352e3f198ea71107a1aa01785f692d5ef64925af58d -dist/2024-06-11/cargo-beta-x86_64-unknown-netbsd.tar.xz=6bf7fc08f593ef8847b3b14a33783fb3c54c542fe61a8a92c0e462a1bbf34b9c -dist/2024-06-11/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=6cc72fe175632e68fefaea683c0569e9fe8f036488154e4f1ccd6a3135479cfa -dist/2024-06-11/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=b27004517d35019ead69bcd1a09c81aa20d2b00b57ad42655f1f434f8d66eed3 -dist/2024-06-11/cargo-beta-x86_64-pc-windows-msvc.tar.gz=e5d5945884554970344d16e90f9eed0dd60f61ed57e48b40fac2ee1d7c035475 -dist/2024-06-11/cargo-beta-x86_64-pc-windows-msvc.tar.xz=421ee0fca436d4d4d3bcd03c242f9312fa620da854b906618a1d8ab27979d609 -dist/2024-06-11/cargo-beta-aarch64-unknown-linux-musl.tar.gz=7406ba1c14e53da0a9ca696dc6c90fb816c072734f569c01f49c6b8f7d542d81 -dist/2024-06-11/cargo-beta-aarch64-unknown-linux-musl.tar.xz=917a2e63fcf03be81375143692306afb54fc2addef9f5508dd58fc1729119752 -dist/2024-06-11/cargo-beta-i686-unknown-linux-gnu.tar.gz=bdbd9c4e2808d9dd0acd219c3ba9972634d73fc4f177468bc196c66be620df65 -dist/2024-06-11/cargo-beta-i686-unknown-linux-gnu.tar.xz=e64d3405f99f8ad74984b81238a088b250be4e079e2c05aa68be1cf84f04154c -dist/2024-06-11/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=bf7c374c02676f9e5a50f4964e6deb4d3aab19ecd66c4d246de0c9a06ebd23bd -dist/2024-06-11/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=52e7907ca99467c71429d03012a3f6cdd95711b9cf002d3aa06ba25b3e53d2b1 -dist/2024-06-11/cargo-beta-x86_64-unknown-freebsd.tar.gz=bd9aace135befd19104b193e09bf982c79c8cb9cce301ff709cde2ee2dc82821 -dist/2024-06-11/cargo-beta-x86_64-unknown-freebsd.tar.xz=c0d2c3a2e56a1f43f62fb73e41b07def93baa278172d822a2a33f571d54f0463 -dist/2024-06-11/cargo-beta-x86_64-unknown-linux-musl.tar.gz=61526c6e4f0ce1a57d4831ac67252cf5a6cef3c106c6953b329bc0aa9c685e6c -dist/2024-06-11/cargo-beta-x86_64-unknown-linux-musl.tar.xz=82bcddea6b3d976e7dee07afe7f805f5a6ff66f0721d281d2c896f352f395c89 -dist/2024-06-11/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=70c5bd18c756123f033a7cec027631f71d7400cb4d0c85d7602a8a6309379070 -dist/2024-06-11/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=f8a6c7dd8ba408a5fe623a0ca1d2f8bfe287b13bc09b1d8ea636ebbdec06c557 -dist/2024-06-11/cargo-beta-x86_64-pc-windows-gnu.tar.gz=fe26e842e6e4186437ea679cc75cd37d70a98b1ed0f17ed9ac876aa0501ebd5d -dist/2024-06-11/cargo-beta-x86_64-pc-windows-gnu.tar.xz=f3fcb2b698736b3df87c06ba829a1f09c218e37b51ff1c59f165aaf8078c619f -dist/2024-06-11/cargo-beta-i686-pc-windows-gnu.tar.gz=607f623f30e25541d9e6c8412bffcb9c16f7180250a244dfe1e2606edd45db42 -dist/2024-06-11/cargo-beta-i686-pc-windows-gnu.tar.xz=604369c997e05719d0fae92f7904e6b66f662bb75add1d836cb100e0032526a3 -dist/2024-06-11/cargo-beta-aarch64-pc-windows-msvc.tar.gz=de283125b79d17f4cf24bb608819177ac1637525b8c7e67a66e798f0c0b1024a -dist/2024-06-11/cargo-beta-aarch64-pc-windows-msvc.tar.xz=8228a47ce003faf1edb4a10cf800d3316ba421a97176c97f6952743f7eddf734 -dist/2024-06-11/cargo-beta-aarch64-apple-darwin.tar.gz=8525a4015fa8078921f8bc5f67012c0d5747b7e7111079bffce79f2b696fbd4a -dist/2024-06-11/cargo-beta-aarch64-apple-darwin.tar.xz=038c700f1b0ab543bb765a4b86638fc7eb1e44257a19d5a5457fcff4fc08f280 -dist/2024-06-11/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=16c75d17531e10f1bb9e95c79d8c3aa05549db3029663c69d2f56b2d2f3214d8 -dist/2024-06-11/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=113d97f781bf8d54f457e07fbe3de1d3a0ce0a1f8054a652083b67b81f4cb642 -dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=7856fc9731c3dd7b360eacf422ce358acb2d33a7fd8b16fa190dc77cd9bdb3de -dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=85c930785fe9857458c9256c5a2849ae223c5dd162bd20ccc8021b2d2ff0a6ba -dist/2024-06-11/cargo-beta-x86_64-apple-darwin.tar.gz=aa4aec2cf417a7446c2990d03cfb3dcaab9d99f5ac249cbb955a59a64b5b7b0f -dist/2024-06-11/cargo-beta-x86_64-apple-darwin.tar.xz=01d4bd8dd8a58f16ee31df775d0d7046ce671dc86f8907514e3713a62aadbb12 -dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=6e5c68d0665add368a6574528184dc1d182e7d72b8da0827b69eb9f9aecbc4ac -dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=180a6148f5c5be222d4e2626eed9cd7de2e0f62d0b456e520d1ab7c4931e1f4e -dist/2024-06-11/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=6af892aa858f35708bf9db322abf35967b1e92725e05f6974e1d9ee3cb4c7b84 -dist/2024-06-11/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=40ffc9713504366978bb6f2eea12741400164318945df22290c7b669d3b77878 -dist/2024-06-11/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=9989b0189f7a4c5aa39c39c78be04e7291cc1358ebed597636f404f56980c199 -dist/2024-06-11/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=8c7d7dc2530f25cc771c75d454fd11f3e94c7954bb869e43736860be1f253d2b -dist/2024-06-11/clippy-beta-i686-unknown-linux-gnu.tar.gz=94505902c45770c45a89ea12759bf46cd97efed657b43322d5dd09c13ed26331 -dist/2024-06-11/clippy-beta-i686-unknown-linux-gnu.tar.xz=14830f6fa92b0d814527a9aa5575d0cd529e76dbe2481533b819c735470ad684 -dist/2024-06-11/clippy-beta-x86_64-unknown-netbsd.tar.gz=5cc5f4202003b940a6a62c2384c749b741a9f6d62461ba13a6179464f6c3e6ea -dist/2024-06-11/clippy-beta-x86_64-unknown-netbsd.tar.xz=01f4f0ef78f17e4991ddf3d974ef7b1dc9d56702ee2d65ede4918e9db99ec222 -dist/2024-06-11/clippy-beta-aarch64-pc-windows-msvc.tar.gz=ee6b64a7a0fc8d8482c38d2a263f9a9ed0b9462e6bd748a72e33c5cd7dac015b -dist/2024-06-11/clippy-beta-aarch64-pc-windows-msvc.tar.xz=f91457f5dccfc81a601b075da340bf521e3cc17ba05d12f90c31c7f5b215a7bf -dist/2024-06-11/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=7da546ff13b3e340f2ba22e6995566cb26e487271e3f1b68b3b7dc0bd5122623 -dist/2024-06-11/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=01807b62c65fe33acd05d10191cb16ee1bdeb1bd7b410a9c0cfd8f53006624da -dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=64f41430de84569a7b933db7f0c573fecf99f5d0c6900e35598c94a433c0296c -dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=b30a73278552ddf511ae82620f14ec7324f281582fa4fa9df3fe984510835aa3 -dist/2024-06-11/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=e6b0ac4bcec2871d2a50e644733399e684d7fa86798131243c4022cbe2ee5222 -dist/2024-06-11/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=edc629f04503ce88443ca6cce0b74b5833ce50f428e00a02b544f422577abb1e -dist/2024-06-11/clippy-beta-s390x-unknown-linux-gnu.tar.gz=ac52f8b4a1b6332aca1ad2363195daf571f7f1870ffa6237441dc7928a408167 -dist/2024-06-11/clippy-beta-s390x-unknown-linux-gnu.tar.xz=96b366fa989d2f739a92ee03bcd141b666b942e0b26922acd620e0fea6d44d8d -dist/2024-06-11/clippy-beta-aarch64-unknown-linux-musl.tar.gz=8ab584ad05a7836bebb20be8d84b0dd3a29d1823dcee3897abde3a47a10e582b -dist/2024-06-11/clippy-beta-aarch64-unknown-linux-musl.tar.xz=e7b669a2936a22ed6d2a36b70a6593657488e5ed8d00a911bf33ce51c4c0cc51 -dist/2024-06-11/clippy-beta-x86_64-unknown-illumos.tar.gz=419ef233cfdbe9011d74796e8413a8b6c1f8e6281248969eaf7c9157542c1b70 -dist/2024-06-11/clippy-beta-x86_64-unknown-illumos.tar.xz=673a2f16268fba4d9bf7dca2c0758f3ee60f9754cdcefa36032b8140e6fcddbf -dist/2024-06-11/clippy-beta-i686-pc-windows-msvc.tar.gz=a3c3bd3b90c951ce404a26a59421f00a03e0dc3351d9dd2876346ff096b7361a -dist/2024-06-11/clippy-beta-i686-pc-windows-msvc.tar.xz=c3e7c1cc91f48003e1b68fdf753011711ee516bc2ff8c7227d427dd8ee98a72e -dist/2024-06-11/clippy-beta-aarch64-apple-darwin.tar.gz=ad7b4ac9cfacd860e03f8a41da51b4d279d76b7f3f2f8ac8a048a45f89a7ed04 -dist/2024-06-11/clippy-beta-aarch64-apple-darwin.tar.xz=ca5d2d63b8d7cae00c86383d9075b2292a28d1a85864877a6c8282f5596631f6 -dist/2024-06-11/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=12820c3e8b952dfa931783b550f882dd96c48d3b776fa9cdb29b02d66ad14d96 -dist/2024-06-11/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=394873c6b2499ebc385a11b512c7378b95478a0b6f530c5b313fe86b65a18eaa -dist/2024-06-11/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=8d9353baa4388d02d89b42403e07d6a61f314fc975b8fcd737d8f4edb0ab027e -dist/2024-06-11/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=3cf0511bc5b14619dd6ca1cfeed003effa7fbfecb42fc096a45a211d1ccf419a -dist/2024-06-11/clippy-beta-x86_64-pc-windows-msvc.tar.gz=cfae39e5d5cbc817aa3ba892822fe2ff122df8a648106aaf0ec18b1ce1a02632 -dist/2024-06-11/clippy-beta-x86_64-pc-windows-msvc.tar.xz=1f1f0596bef154eb3fb4e73993794850f8ad903f4dd0ef9697aa8d5b4d6ec2b7 -dist/2024-06-11/clippy-beta-x86_64-pc-windows-gnu.tar.gz=0909caa93377b2aaf2a05a345e8cfea2c69dc27bd7f7d7a9515576e90cd0aad3 -dist/2024-06-11/clippy-beta-x86_64-pc-windows-gnu.tar.xz=9125bfbaae2103f176b1e1b444fd38c7e74d3414536370037ffc6a85d8567551 -dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=3721470fcc17f8d37c66281bb66c65a66181c80775571520fcc2dfdb583dc640 -dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=79ec48af2925aaad12be405fb72dc9bd9e2ca89fd3a6017111020570ddeb6585 -dist/2024-06-11/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=d957f503dd4d9d228b6a445eab1183dbc6509a49ccbd475d25143cf1dd28f03d -dist/2024-06-11/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=da7ec767b4a85fa327c957888b228671b2311286043165926942504239008d3b -dist/2024-06-11/clippy-beta-i686-pc-windows-gnu.tar.gz=51ff631084254be43d79d7791000a14ad9f11239e42be73f48383f774109c2cb -dist/2024-06-11/clippy-beta-i686-pc-windows-gnu.tar.xz=52079acb4f908150d207a838cc7d3c15a725c1d61be35d0d0b2ed85b3386f3fc -dist/2024-06-11/clippy-beta-x86_64-unknown-linux-musl.tar.gz=99a534de9e5fb552a4e7a10eca2645ad260a723f42e15df3b4e2bacf82e90348 -dist/2024-06-11/clippy-beta-x86_64-unknown-linux-musl.tar.xz=7f9e6da187bb71c384f5040298c43bd303fcbc5e3034dd7fdc7b251c5d7b3a34 -dist/2024-06-11/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=acf05b62fc219ddc2fc4e4bca2bc4c2cca24398d6276bd7e7d9f8504627094c4 -dist/2024-06-11/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=c737aaf9c4bc3a743752b13159902fc5a8ce746f44221799bbcbcb1f9d04dc4c -dist/2024-06-11/clippy-beta-x86_64-apple-darwin.tar.gz=422e97d9081e66a46ec3f4c2a7ccbf9b6443c7b343e0a2e90845f233536f545c -dist/2024-06-11/clippy-beta-x86_64-apple-darwin.tar.xz=c9d02836c9d0e48684f0a343d6935848ec75e1faaf7007a6a3e0718b71e77f7b -dist/2024-06-11/clippy-beta-x86_64-unknown-freebsd.tar.gz=f207fbfca76eb22f7097eb98ccda1da513fed417371a391e43e8d19fad9b76b4 -dist/2024-06-11/clippy-beta-x86_64-unknown-freebsd.tar.xz=61e39b0aa88a1d8e131a461cb3cebd05154c261058b735f29dc6ed603464b5a1 -dist/2024-06-11/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=c68d27b08f9699c7c2cff9e9bdb7485991d8445074e8b84c2e2222fbff209f83 -dist/2024-06-11/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=d5961f82b9688b9c1ccdcaf578b8325e8ac1e0cc2ffa02131fcc3281cc88dcfd -dist/2024-06-11/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=12ccb5b1d82ad22292b337c49d1c5b0c0d584cfb4e649faf6c53471915df2473 -dist/2024-06-11/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=80fa7cb902135006046c10db64d9060103dfb1452e8e0ef256f8344fe0a9c8c5 -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=c08499ebec188f67477d0c4eac2e172b59f6d66ad4becb36330b5066af6547d8 -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=b3f29c79fc8bd64a2e62101468bbaa28338f444839659d4f99df34ccccad5eee -dist/2024-06-11/rustfmt-nightly-aarch64-apple-darwin.tar.gz=4866e6d478424fb1c01b2b98678107b9c89e67a2cce3db49833bf34007f868b3 -dist/2024-06-11/rustfmt-nightly-aarch64-apple-darwin.tar.xz=e8e6fd846f70a99e6fad735ce4f4c8bca5f6b5f2d63364c2f8a7df85c13b63f4 -dist/2024-06-11/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=f14077408a763f0fa6218ed97d7697499c2bded289a5953336c727e2de93bd92 -dist/2024-06-11/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=27324f62ecfdeeb4a66ce3c62219c491f04ac3822305ea771df0381a5f4e5418 -dist/2024-06-11/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=1147008dfa30891e264bf5f469c9528fe47bdfc729a12215e5a985179513cc66 -dist/2024-06-11/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=95fd945a6c9bcbc9549913906a8f2de20adf54b84b82fe8d981dbb5b767f0cfe -dist/2024-06-11/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=4f423c5ce278204dce918ece2af84bdb55429b7a11f630de95a1516e7b113f53 -dist/2024-06-11/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=c86fe94c9b6cbc56285478ffa715877d234af19f1b58b3fd4b2af23a0267b657 -dist/2024-06-11/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=f1200f077402474049f6b817688435db0ebef7fb52ef645d5b5d2f7ceea52a08 -dist/2024-06-11/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=a0d801ec3aa93389d5c6ae2a95be25ba3a50f53182c0a84a00c38a2f708447e4 -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=d4e28ad9b054cdd19fce6bbc37dc86210b0536aee5c23bca53a6bf1340fdb5bf -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=b0958ab975026e09ba921234353f5a81b3227d23c10ded4be4fe37831e6abd12 -dist/2024-06-11/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=f1caa15f16aa1c93ee783bf273d0ae6a7abd0b38c93c717f2d49af5444cd4e09 -dist/2024-06-11/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=130687b9de22fb1d0c4c845cff5475e5959278718e3b6a1af7f1b9e8869b527f -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=32e3881c61abfff7f9c006083045ffa5765c0b3a5a6ec46607e5428423ab76da -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=c1859b8a44306b5b3ca27b9226bbf351cd3ff3cacb4faf61200d7baa76009367 -dist/2024-06-11/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=4f5c23ba3426719c39a7c767be8db83382c84ff7798c5194aefbeda821462269 -dist/2024-06-11/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=18d9a2084cd5e55792f3841fc7d6602bd4a973726662b516289385b7908ffa4c -dist/2024-06-11/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=df5478f63a49c55ffe9cf62a1882feeca019290c0cc9a9ed5a4122b8aa1325cd -dist/2024-06-11/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=f54c3b8febfa4023a0334dfeee59d75a44f5c1d3a02d2d57959473d6ad51205a -dist/2024-06-11/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=dd2d83a7bcd45a0d5de312a2a4a28b2471a9597d318135214b45a69e1e449f91 -dist/2024-06-11/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=02a8ada9d737e115033d26b0691f2ef22428ba2f67b7fbab7c4205df8162a96b -dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=20bde0cb3ddca790c0d126d5d590b9a35a1c8631b42fbc2d4e85cfe5aa880d04 -dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=dd29e278f79ce1be14ad44d61039e5a6a63e0e597c4736ab153beb758ad37fb9 -dist/2024-06-11/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=09d98f5c61def16b62b980a6cef3b1b3704cde5c0ad2fdd7d23521db410b6b8d -dist/2024-06-11/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ef43e61c2d3d90b09494a8dcbb5d2375223fcba6078f2bfb02ea43750b1a76a3 -dist/2024-06-11/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=80318fdf7d55c71beb8ff1434b79776c59008f5d62c63530da9f19a67cc18484 -dist/2024-06-11/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=05ae6d4f8ed07cc75b333e606af14f239978abe197b70409a5f2aadc7de43a4d -dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=a079a478e9a89c3a24864b44670bdffcebc139bbec9c54e7aab6394c08bcaab4 -dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=243c2d7c23e8aeddf9729e1f7f9add6f88f50d012efa02644030110cba111713 -dist/2024-06-11/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=76b69b4b25e8c0d47b80491bcbb724a25c02d95e7f9cfe7a9c3c3570c24363c1 -dist/2024-06-11/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=027e075c94f53403f03a4fb29d4575928e1cba141fc0713c93edaa4ea5917e42 -dist/2024-06-11/rustfmt-nightly-x86_64-apple-darwin.tar.gz=b34d3f33e7ae81a5a95927997658d738a3d39db01408dad2d9fa72efb539c73f -dist/2024-06-11/rustfmt-nightly-x86_64-apple-darwin.tar.xz=7efe129502b732219d1d24d59584b5ad002e7dc5288e047b06bbc81fab36e054 -dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=231f11ec1c6e4f57556aa32226d22328656d2ceab075cff67c8ab0a1ec32a823 -dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=b1cd7aa45a516df3a68814ce52753eeead4134943d610449456a30c682079ad0 -dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=3fb86c9281ae8203e1119bc00e8001afe7e894360adc791a9a0ca1d50e6a9032 -dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=6380fcfb015a40c4605ff18f8ba54c3be9a8e6a0115ede378565d9cc9943c627 -dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=f3ff749dedcfb259a4b2d66ae30245caeb79fdec3b41a3bb4f65d50b1cf18120 -dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=1ca50ca8ff36df7433a4ab4c8a63c3c91e214f6c7889843edfb9120c1f4f68d4 -dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=a7f5f171dfef5b67498c93354d9dc5a7443d69f3f9bc0bc56b660d1450e9b7a5 -dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=dff02f7a7e9ae58c41faf4c5731862d11fee06d17099c1ed18863219a4a82815 -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=71dc189c1d29d3fec849b47f64deb831569515e56821511b1221983eb9ed99a9 -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=17736f80644c8cdf8239940812962e6f55c1924896f79bd2581d20582a9c752a -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=b77e4a1e42c7169d91e268972a9195f7c8494e5cffa5fc71720df799a4eaf74a -dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=8cf89f982aaa5559f2cd1721edfdc25f5e99518c418e1a6818c566d668c8f842 -dist/2024-06-11/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=7703c27800aa2d871624563380596a18deb90081e38cdf6c05cab8e794e5805c -dist/2024-06-11/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=ca9dcda51c8945db32e004424eaf26d4f541a51a78be87be0063e5a0b06fdd4f -dist/2024-06-11/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=59a2173462baaea0cef3e0f088b4e6ef64e2fe469d1833798d92e844f0126902 -dist/2024-06-11/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=0cab2fde15bbfab741eead0b084886f8b640aeb181251368464bd833fd08df73 -dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=8473ef60ff1d5c7aee90b8e0a984c9be7e095446d87e0f13ebcf10f245c0ef0f -dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=657a55f27794460bad174c4f9412e771c679c6b608cc3dbe32903efb075418da -dist/2024-06-11/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=bea7aff88f7d7622a23e91c437d9616a9f1adfbd56ddcb42ee33589d064d5161 -dist/2024-06-11/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=a5ab0850c71e322b1042815c443c79505f1daab2b4a69f8cce05e3761a24f10c -dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=8c00db9c85fa8053f78285c8a546afb32d6b864795388f1ee7a6a49d12643625 -dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=c137f5f943d1c3f14a4a8b5eca2b541c43c82738452646df524e57a6ef0cbd3c -dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=a4087ded3076e4d487efcd4a957554255ea1fad5052a01529ea1847559f7b40f -dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=ed2b01ad985668564ba2c3f87dcac4f5c4bcf7ea0b5ecca6a30c1f1c4b63a4ef -dist/2024-06-11/rustc-nightly-i686-pc-windows-msvc.tar.gz=41db0331bc8f9d164d4198c93234dae41c1b9d2f53d466ee8cdfba4f9704c45b -dist/2024-06-11/rustc-nightly-i686-pc-windows-msvc.tar.xz=49766cd2ad581f253f3fc0ba4f755a4aeaae954c75d41fa29a9302568e78aa75 -dist/2024-06-11/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=b57be1a1dc9e70c47564253877bc0cbbdde8a8064a6a4bcf68f8adc6b4c5d4df -dist/2024-06-11/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=cf2c4651297993671f033eb9bb9877b157638bc41cb40ccac3294c3fd13786ef -dist/2024-06-11/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=619c2eb3ed3f153d2c0ba6cbeb7f0e72c67fbb1e40c025752f94b46c9b740426 -dist/2024-06-11/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=105560769c3672133ecb063ac0a8ef541229490401470180af75151403287d3a -dist/2024-06-11/rustc-nightly-i686-unknown-linux-gnu.tar.gz=211498f7eb884908c8110ce922183bbd6cc05d96d9c8b5c9068dcfc4d29010cd -dist/2024-06-11/rustc-nightly-i686-unknown-linux-gnu.tar.xz=61beb47b917ce644defed991afa6d82028cb8f1e6a191cf5c84e8be883ad3870 -dist/2024-06-11/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=06036f5bfc00950e03a66e8dcab0eb7420fd055aadb85bd42e11845eef122122 -dist/2024-06-11/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=39d3dc95b1e0cfcc381d27540d100b8325728ef9a6a8b72a0e4ad85e2e957a63 -dist/2024-06-11/rustc-nightly-x86_64-unknown-freebsd.tar.gz=9b7badc1c8443571aec7b403e890d14846c3972b02159c8aacf3157ede69af9a -dist/2024-06-11/rustc-nightly-x86_64-unknown-freebsd.tar.xz=a1fdcbe1ca277b22c2dc8aced8ac0f34f841e7a7512fe421c2b045608c4ce07d -dist/2024-06-11/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=5e6105bc6304ea630a8170c785df11cd435521a2ac90348284898c4aab6ed5f2 -dist/2024-06-11/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=97dc7091b1ffa6a23a4b609a680c1f2f63debab80b5c01bc90fe271d6599bf89 -dist/2024-06-11/rustc-nightly-x86_64-apple-darwin.tar.gz=64769377c3714749acf6404d75f46b00ede0107840e736432d759dbfff15b6f9 -dist/2024-06-11/rustc-nightly-x86_64-apple-darwin.tar.xz=8ce829c39fdf21467307ee540c6b06b510d45c76c395947afdc2e5592d775977 -dist/2024-06-11/rustc-nightly-i686-pc-windows-gnu.tar.gz=4c9425ef2d5fdf9badec75783fe61ce0a251fde672c993c5b0069182449e2d7c -dist/2024-06-11/rustc-nightly-i686-pc-windows-gnu.tar.xz=442467ab4199e2fb62c69a96cc1c9f03478a415877996f0fef20c54760b2e8b9 -dist/2024-06-11/rustc-nightly-x86_64-unknown-illumos.tar.gz=0b4c3520ff6e5b053a6b37f7d3708fb115fbb99981f337a1786d675e70034acc -dist/2024-06-11/rustc-nightly-x86_64-unknown-illumos.tar.xz=d192e69dd328e6f930ad5b578065ebab12913762845f68ee55b43ddc9ba074a1 -dist/2024-06-11/rustc-nightly-x86_64-unknown-netbsd.tar.gz=0828f2d9395cdac8e9db6ad5c6b3cab3587fc82bccdec9962cd096e43e4f7793 -dist/2024-06-11/rustc-nightly-x86_64-unknown-netbsd.tar.xz=542f61a4c5a719f2583b0ba820207dc9d09cb9a2bb802da7cfbee5273bbfddfc -dist/2024-06-11/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=289b1bee22b8d9d84e92461694c6558476f944ac07caf2d22f0c4ccf3d29dbcf -dist/2024-06-11/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=23c86fc06ed9e7bcf8da72085ff88ec224ccddeca78ddc31032b314cb254ad64 -dist/2024-06-11/rustc-nightly-aarch64-apple-darwin.tar.gz=cf045620e63a5e623047490af956e0daf0bb321ed6a215ae355b8829c047e58e -dist/2024-06-11/rustc-nightly-aarch64-apple-darwin.tar.xz=0abec976185b9573dbc91868ab4fb84ba2837d25f3fa1d74b3a193609387f3fa -dist/2024-06-11/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=0b1146e24e4e59e4577b1e40d3ca45096864e87bcf9f258a34b9d9cf5abc3eef -dist/2024-06-11/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=289e97314fd22da08c8943d1240fa94b1cfd2cdf061dea15bfb582a9d482d6a6 -dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=8957493cf92d57381b35d6ce5bbdb408a05c2a6becc8e2cd37b0502e3ef35ffb -dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=9015e492f773b91054c53aca92ac704336504959779e4b916d42fc966e6058ea -dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=aea2dc3831864dc5ea2c389c8208fc82baf1566f4e5688511495db25525e6020 -dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=9b6f1c3b1656ee2037afe170d6909aa037d484897a83679e2115392a35559062 -dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=3a962a42e427675df5d8fbc4e638949ec0669a95ecabcdddf5103a9c19f291ac -dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=6fe48c92425b41ad7c3e812abe2e5decb117be178a16f6910bd60000fe8f355b -dist/2024-06-11/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=0a3147dd45cbf54e66b92baec479a5550c63327185baef3df9638c5a740e0921 -dist/2024-06-11/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d35948d49fa1e526434ca89c2f7542fcfeb86b4ec35bb92f87f387ec37f4ccda \ No newline at end of file +dist/2024-07-26/rustc-beta-aarch64-apple-darwin.tar.gz=a265d7831284938f090ef34cf2fb374f4d2b946c52829de8e53a06b7373c3d7e +dist/2024-07-26/rustc-beta-aarch64-apple-darwin.tar.xz=f2420846f82f474892776f0bea176fbb2f1850a1fb9f51a1afe40ae6de8bbc17 +dist/2024-07-26/rustc-beta-aarch64-pc-windows-msvc.tar.gz=e3e0e2b8d1f8804cc2b5785765cc6807bbf7120fb010d74453097b5931c82e96 +dist/2024-07-26/rustc-beta-aarch64-pc-windows-msvc.tar.xz=1270aaea2f20a378e68d26518e1d34810ed346a4d4c272138c8aef67e3c386d5 +dist/2024-07-26/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=02bcdc3eed81ba17e485556f1c4d5a00e851afb75a20f860ce7b6d22cb8f9e99 +dist/2024-07-26/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=52b72cc7270fe75e023ca9c40d095a84cb0ce31657357cd7676d6570ca59bc1c +dist/2024-07-26/rustc-beta-aarch64-unknown-linux-musl.tar.gz=b47ba6f2214f238cf8df3c386765e7816bc86e1166f27ddb6c0ce678ec716939 +dist/2024-07-26/rustc-beta-aarch64-unknown-linux-musl.tar.xz=95537778025708922b4bf27f34f0634a61a1d963a8424f8a06c82564c74e849a +dist/2024-07-26/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=d79c8b744d00b174be7630a54f9ee23562f8977d7e09e2ee46a9d72a80eef514 +dist/2024-07-26/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=0f7d3d0773a3c1f94e8252d95b134f7f5e727a884ece54aa0f77e5edec5c6e29 +dist/2024-07-26/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=5e0d51532c4bb0c29fd01ef5d931c229f711c9326df0bdcb2b88c75aa72dc210 +dist/2024-07-26/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=5fe623cab0afd5f7bdd664a593ac02791915e30cfc9ca76a02ffd70e1e865ede +dist/2024-07-26/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=1df1e1a44170d6c9ab4988e9841ab56ee00248ca5cbf7e38b69677bd5b2de585 +dist/2024-07-26/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=70791fcd1aef6686e012740017a0e5bb4441eb4018f875e8e50d5597c183291d +dist/2024-07-26/rustc-beta-i686-pc-windows-gnu.tar.gz=045fe2478c5a5341d7b7a05b9df23ef8f81ddbe0faa78e3f2107341a0e8189f6 +dist/2024-07-26/rustc-beta-i686-pc-windows-gnu.tar.xz=d7f3888692210b6024ed72864015d1c101f28814dec5b60c9e32a772f11f3b47 +dist/2024-07-26/rustc-beta-i686-pc-windows-msvc.tar.gz=5b2988c87d91013cd92ebca43fd6cf5d49250fc0452830ea46622e418984041e +dist/2024-07-26/rustc-beta-i686-pc-windows-msvc.tar.xz=b9d65cca6691baf2841432d6d2d566face171c460cf161f39a21acf7c0b033c1 +dist/2024-07-26/rustc-beta-i686-unknown-linux-gnu.tar.gz=7b037163f1c19a0fbbf4029d12a7b3764e747c9ea7944f1e45a6f91a068df224 +dist/2024-07-26/rustc-beta-i686-unknown-linux-gnu.tar.xz=f5044d8f3784e5bc682b03ccbe7389abf5b254a01074fa67eb824d2dfc9fd3ad +dist/2024-07-26/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=840131ab570c68c0d46244eac743f7d8a1f470d1926068868d288307a488b3c9 +dist/2024-07-26/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=6513a144014807bd258d2a90fa4618aabdb4e265268e408d5cb699a610b78c31 +dist/2024-07-26/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=d703a904da3604e553ba0a74775f8005e9f52615be3266197b71362b20414def +dist/2024-07-26/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=8ab79d34b3c182a4349fd01a50aa13ca064ecea64c2ab09f7a9ec38046741c7a +dist/2024-07-26/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=70eb8f65e91068ac20366fca1fc30227f36cb2b2ce1abdaa72504583cc7865c7 +dist/2024-07-26/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=3372e272cf9620191000a95fff23e417586e6b894e211d44e4f658c241c3d014 +dist/2024-07-26/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=e8f09f56c43ae6e99a42a8c430492157415fa6baee2c3f8f86f94ab4279634cd +dist/2024-07-26/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=599fcd5c57687e29eec085f7cc2733965a8e8fe5eac6499abd92ffda64f6e500 +dist/2024-07-26/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=904029f0c81ec9679851d952f0f0f616cc183028882c6686f5b5bb665ca00cef +dist/2024-07-26/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=31f3f895d665acf082a6dbb40e91fd97f823f84c8e62074dfa82e60f22606f5b +dist/2024-07-26/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=4d374e60b8ac2f3fddd5ba9b46ab80555bc5b19f080b86604dde994087b00ed9 +dist/2024-07-26/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=c905a880b98d1eaabfb306d5b6dda018e5025b42028cbb12bad408f4d469f3ae +dist/2024-07-26/rustc-beta-s390x-unknown-linux-gnu.tar.gz=69f0228799685960f36f31eb4c139cbd7925f5180468e2d4a435ecbad63e9535 +dist/2024-07-26/rustc-beta-s390x-unknown-linux-gnu.tar.xz=1da1caf5b2ad149ef82af58278dea6c381c372366c48cda8ca7f520cc8752b2c +dist/2024-07-26/rustc-beta-x86_64-apple-darwin.tar.gz=81fe4bef33afb3010fdd7af42653fbf5dd9abf0d69268576dfe144c88c7b9944 +dist/2024-07-26/rustc-beta-x86_64-apple-darwin.tar.xz=c4785475e3f7087cbab405937c0ca3ebc08455dd36df156680cea1ed38fcce5b +dist/2024-07-26/rustc-beta-x86_64-pc-windows-gnu.tar.gz=6e58397829f67bf952037c94894409032e52ac8ccdf3858346d131fff6175a50 +dist/2024-07-26/rustc-beta-x86_64-pc-windows-gnu.tar.xz=8e834602e7cd1fcd585f51ab280266b4ffc57e0c974a1a26b901701b5127135d +dist/2024-07-26/rustc-beta-x86_64-pc-windows-msvc.tar.gz=94a8a08495b8093db5d9a32c3258c9f97ea8b0f57947d7ca642664c06cfb438d +dist/2024-07-26/rustc-beta-x86_64-pc-windows-msvc.tar.xz=8d057c7167cffeebeddabf25f08a50de8dbad5ed7cfffce625c81a99d757e998 +dist/2024-07-26/rustc-beta-x86_64-unknown-freebsd.tar.gz=dcc93e7b76ca7a469544597885a7b5f4b5f822e05a89b2d7a093e06dede9f52b +dist/2024-07-26/rustc-beta-x86_64-unknown-freebsd.tar.xz=3efb7d4d97dc9b13a0178464e79bbf96bb71763a588b48c587746acd456ceb8d +dist/2024-07-26/rustc-beta-x86_64-unknown-illumos.tar.gz=ef5a0d34b78848962e83ed468934d7cbedff778a89e50f2128ba25d0943e25ed +dist/2024-07-26/rustc-beta-x86_64-unknown-illumos.tar.xz=5cccc7bbb15b63d422dfd2c8ac25e2103906880e843feccd318ba8b8c614d3b4 +dist/2024-07-26/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=a9b352f61f8124c1fba0c76ecc8c1c972e61e97f5299f3b86bc17f035351ced5 +dist/2024-07-26/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=a8fced9a5de02bb660491b440f6596e4baf6eb9114b8dd8d93ac7ada6266d274 +dist/2024-07-26/rustc-beta-x86_64-unknown-linux-musl.tar.gz=5e6fae2fd75e1780995fa9839593044539fac3f095c8948ba446b7024f632ec9 +dist/2024-07-26/rustc-beta-x86_64-unknown-linux-musl.tar.xz=9eb16965a7f1f7be7751fb61280f272c2497765e552fc0dd653700d551a04539 +dist/2024-07-26/rustc-beta-x86_64-unknown-netbsd.tar.gz=40b7312ce424dcbde5537da50d17854c255c3ed535a124f4f0102cf62bc86b5a +dist/2024-07-26/rustc-beta-x86_64-unknown-netbsd.tar.xz=222a86f97d9bf2db38ebea9f45901790429658154d3448d05e7f0ddd9833ff18 +dist/2024-07-26/rust-std-beta-aarch64-apple-darwin.tar.gz=a9b3cfe03b3053cd533ff02fb54f95db009396802464f32840b3369e9a3e6afc +dist/2024-07-26/rust-std-beta-aarch64-apple-darwin.tar.xz=9ddb6b8291ade9224cc76f4d946c179bdc6e25f5819a4ba1ebe1f207d4c663ac +dist/2024-07-26/rust-std-beta-aarch64-apple-ios.tar.gz=8c129310921ed45d3cdfc30e60d93403adf8158a6661de96ee5d4a63119cf3e5 +dist/2024-07-26/rust-std-beta-aarch64-apple-ios.tar.xz=e1fcee4b062f25b0350e5e74064615c354ee8b399adf792d46319ee782857398 +dist/2024-07-26/rust-std-beta-aarch64-apple-ios-sim.tar.gz=40d6d48dea64407bf31be39f9e0316a1e47648ee75bc0d3361edb961e062aa6b +dist/2024-07-26/rust-std-beta-aarch64-apple-ios-sim.tar.xz=3e01648f45e0c7cb040d3c293024477801930d5078d54c6291ba13aa8eb4e1f6 +dist/2024-07-26/rust-std-beta-aarch64-linux-android.tar.gz=da310568bee0d4868132589399c9229df5ac575a028f2409f51fd1f8c9e81779 +dist/2024-07-26/rust-std-beta-aarch64-linux-android.tar.xz=f3079ebecbe5b9f23495fe0966fc39fee1d38869287695804c3c04d53df07496 +dist/2024-07-26/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=9dce2956e4b01d867ef6c229dbff9319a779a80e43a29fe1f8b10a333049264d +dist/2024-07-26/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=b64e7b87027dceba21283ba71430a8053a918bfaf6c071cc70d5f5da00155944 +dist/2024-07-26/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=0d103e0c4ec8039dd42be09bb9c74f269a9567fe7dfacfc54106912cd2aeb35b +dist/2024-07-26/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=4b3627e9e363e9b2fd85222c677a7250b12a5d32919f38a31ed7375637a79a81 +dist/2024-07-26/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=061fdac14ee0bd3abba36269f68e9ad25a243ac7062f946155f98680f484a84c +dist/2024-07-26/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=8acc12f14ac643b822595a6a07c7e5ac65855b83938ac122e3278f64769eeb13 +dist/2024-07-26/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=199498df7d94989d4ff1773fdfe86e97ac99ead487f402d020f78688bc6dfba4 +dist/2024-07-26/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=1337c2f00e9bec2e0a553234d74daea67a9781459d0abe3c8de954981d9354d2 +dist/2024-07-26/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=a7116c13a413989f2ed874d45780aced3299c4ef8ce8e3bc99efe960a73cadb3 +dist/2024-07-26/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=80ef87143ee2339e90842c4893284217ddfa56926524baf94341277c1210dd07 +dist/2024-07-26/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=c47cab4bb196e749f07d03f6f6c8f2fe538c84a055a298f0b505ab98dadf4a1c +dist/2024-07-26/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=b36aa3f0bab63ac21fb69610d379a87fbd1ee87041153eb1e6886a237d22b171 +dist/2024-07-26/rust-std-beta-aarch64-unknown-none.tar.gz=92de36c897dbf90f9986396da1f8a954ad7b4fdab3e466d63974c82611e76d47 +dist/2024-07-26/rust-std-beta-aarch64-unknown-none.tar.xz=fb5d50ce34bd222c1b624e5aa2248776f8926f3ee360b7c4c1e0bd412d01bae2 +dist/2024-07-26/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=fea794a4c8aec3e5357199db7a9b701ceedded1dbb50167b002d368a6ae09cc7 +dist/2024-07-26/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=47fc6e497bd4c8013f708ed052701129c308b657d28ee2dce877cb63ccb1b06b +dist/2024-07-26/rust-std-beta-aarch64-unknown-uefi.tar.gz=17eb45b10a0c9cc702e7fb4f0c7926591d3ad0f052f2bded9cf852ea082cd2bb +dist/2024-07-26/rust-std-beta-aarch64-unknown-uefi.tar.xz=1f6b1a3082fc29546e4d8dcb20675f62ead20fe96112998dcb1260e206e93cbc +dist/2024-07-26/rust-std-beta-arm-linux-androideabi.tar.gz=baad78bda532ed77dbf3e4265595b9dac82f6fdc27dc6deaf7386a60b981b927 +dist/2024-07-26/rust-std-beta-arm-linux-androideabi.tar.xz=cf4283b1224d4dac3c55da905608c5bba7639b6511977f3de344a999ae39e571 +dist/2024-07-26/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=0da01efb79d43140515c48561cbe7a2fdb83895fe4351f2fffaaf108fb163268 +dist/2024-07-26/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=baa52bf16290d71b1b89f12ddcee5adf18c480071f9fd4daf3fcab72f92fad4a +dist/2024-07-26/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=2325e8bc225a24a0a8c16da6dded16d4befb671b913051a7cce12ee3e183eac7 +dist/2024-07-26/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=ab9ad0ec7057deda5c29e5d7de317eb32512fb95b62ba628b91b5246dde9c90d +dist/2024-07-26/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=be6d9a01609152eb815cef9462023f0ca2f2950ed03d914629d8e6e3a96af8ab +dist/2024-07-26/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=75ec6023cf9a43ec7ed0f52a1633b56154d9505c5d5d769153b48b9991ce99a8 +dist/2024-07-26/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=cc1d361b775e966c700d122f925a9f2feacecaf7d2891d9c5c5a4a1060336408 +dist/2024-07-26/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=934c10c58db5d7644bd0b7df6c3a128cc53b88b412128c33de5932fa0194f4bb +dist/2024-07-26/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=949a18d9d8b4c7cb3442ec4e387c91feddf0cc26f1fe602ec6c2c484312c9845 +dist/2024-07-26/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=7adb9a93d35002d0335b3449b8d9af35a80a35524fe98bd86c3896481f020597 +dist/2024-07-26/rust-std-beta-armebv7r-none-eabi.tar.gz=b40dbc6ba0f3f80414bdc11260f291f067acbceb50d84597a9366746cff53348 +dist/2024-07-26/rust-std-beta-armebv7r-none-eabi.tar.xz=1564c3215b173956bae41cda7312ed7912812e0fbb65dd8d75808a1d5084fc8d +dist/2024-07-26/rust-std-beta-armebv7r-none-eabihf.tar.gz=809d7f027aecc4a131921181418e66b956ad0a2a7170a3ec3649e21659c5a524 +dist/2024-07-26/rust-std-beta-armebv7r-none-eabihf.tar.xz=1b9589a72d2902cd4491406d93c599d2e850fa2ba19b1bcc6bdc5cc7196f9643 +dist/2024-07-26/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=f2840d36b089fdbcff5f6f9f7a6e576cf21836824957cdf2d1ef2c75a1f5c11f +dist/2024-07-26/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=5723cf48a1ff47fb764cc5ad921356e490997aa09ecd86404e370ffdacfa4b31 +dist/2024-07-26/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=fb33110ad649aac6f55310e6f03d03d4d75ccdca43ada93fb97a717975ef867f +dist/2024-07-26/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=cef39c2886fffef2986e9fe8f3be58f0d7f3846466a35443bc0d49464bdef98a +dist/2024-07-26/rust-std-beta-armv7-linux-androideabi.tar.gz=68fe7b30550cf1ccc44a1669f26620ae418b48cd6283b716aff3dee91acfcbec +dist/2024-07-26/rust-std-beta-armv7-linux-androideabi.tar.xz=81f24aedc06a25c8dfce729132a77e90adc5fe2aec86a3a9548aec9458127ce1 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=a63c5d40bcf8b0702c1a91e992c042130916edfdcf94fb4615ba10c89dad6642 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=4706424b53063b768223880d87bbec0cf57e524374a6ef053ebef02d096484e4 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=85c3684f175642463777aa69889a20391e39afc0d6356baa3ac0445f7aa25119 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=a8d67436a8ee232d57e2924f8f9e5cb0050c71740d57a4e677674adb75093f31 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=b3226669f49ee8fc89c1f84855c29f8416df3e32d3276547f521f738179164a6 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=9a5711743a0984ab7baed2a50f87e50d7facff2ef05f5b936b7b25f5a5992734 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=fd2db70418d432c7b32bed92f5af0f301baa3a71c171d0ee8b0798bbc8e239e2 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=97922d70b37e1e2ff6dc447625b3482094824910a1d7a483cc707985406560ee +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=710be82238e9ad617b2d5f52ef37976b415e7b6ba3d5f3677080f2ccf78b9d44 +dist/2024-07-26/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=2cd0956277cf963f95ffa0104bdcd60cb4e6c4bb2a443a683d67f41726408159 +dist/2024-07-26/rust-std-beta-armv7a-none-eabi.tar.gz=dd38446053d1500f3bdc325ae5cd52d0434c618d79a7c0a0473c76691353bac4 +dist/2024-07-26/rust-std-beta-armv7a-none-eabi.tar.xz=5b82a2f970be59563cc4f550e4202be802cbe21dcbf14de413ed8f30b0e22020 +dist/2024-07-26/rust-std-beta-armv7r-none-eabi.tar.gz=9758806233e59d7d40eeda0740559d9838f9a6c608309567af61245ea57dc212 +dist/2024-07-26/rust-std-beta-armv7r-none-eabi.tar.xz=4266b69d61a46c0c6f6d3aeb2b3378ff88201039df50cd8d3a51c9ff910be181 +dist/2024-07-26/rust-std-beta-armv7r-none-eabihf.tar.gz=6142ea4cdff99902181379fdb9a651fd9d1ab6276ebc104e73b2280a6d9f380f +dist/2024-07-26/rust-std-beta-armv7r-none-eabihf.tar.xz=bbf4c74f6b6a32f01d03bf101f69dd19b909717036117064fa2c8cdd0b4146da +dist/2024-07-26/rust-std-beta-i586-pc-windows-msvc.tar.gz=2cc32752932477945f248dee4d277754928b982815e2e2e0d27883522fb9b8d2 +dist/2024-07-26/rust-std-beta-i586-pc-windows-msvc.tar.xz=e40eae93c31c1fa06517a21cfbdd2dd18e2f740aee1251bcc9bdc680a565241c +dist/2024-07-26/rust-std-beta-i586-unknown-linux-gnu.tar.gz=6d7210503568d9fa02231ac2d9a1b400d00a37f38ec8f00bfcfe45c325aeed02 +dist/2024-07-26/rust-std-beta-i586-unknown-linux-gnu.tar.xz=cdd81201e38226750f2b4292b099b5d78fe0115cedb4c28bf20200144297d8b6 +dist/2024-07-26/rust-std-beta-i586-unknown-linux-musl.tar.gz=95244114ed2d6dd2de488f49115b11a25b008134f1fa1c8f2e2a62849b51f213 +dist/2024-07-26/rust-std-beta-i586-unknown-linux-musl.tar.xz=fcb3280dabb4aae920a14c7f4fec52dc3f11bc68f5143961fa88347aa020bb9a +dist/2024-07-26/rust-std-beta-i686-linux-android.tar.gz=d36c3baba2df9d69e197a773f752bc36e107c2411d78037a357039a856e35aa5 +dist/2024-07-26/rust-std-beta-i686-linux-android.tar.xz=14b9bedd1bb95e1bac2bb028670b1d9dbe6998bb6ad0cfab1db0be0cd9e3d4cf +dist/2024-07-26/rust-std-beta-i686-pc-windows-gnu.tar.gz=44463b4a17c87a2de8554e2629eed8172b60b078ccca7e78bb2b0a20324f51ef +dist/2024-07-26/rust-std-beta-i686-pc-windows-gnu.tar.xz=1e4c3a1d8585aac5aa3e66541b4aef03a0b429a6067300c58f6cf588eac19fa6 +dist/2024-07-26/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=b1d9d5967c039721eda402a7227074ba6e6e8f2877be49eed961ecbce00004ed +dist/2024-07-26/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=2436343b99d81abae0e6bba16850dbc1c005ebba5defb991c3a0e99834410746 +dist/2024-07-26/rust-std-beta-i686-pc-windows-msvc.tar.gz=c6147e9c6c39501d133a344972dbb8108bdc45bb2fe8ad624162ad04a9c00c6e +dist/2024-07-26/rust-std-beta-i686-pc-windows-msvc.tar.xz=6207bdfe0796e4bd4e7d25457d67559f15aa2e8bb91b05284ed147f8799de869 +dist/2024-07-26/rust-std-beta-i686-unknown-freebsd.tar.gz=c675df742bd9fbc01f67411a19926e2814412f12e09b53d1492e8c7250f3e685 +dist/2024-07-26/rust-std-beta-i686-unknown-freebsd.tar.xz=d5b9c3783f6d2dbdf25e0495e32a295c4820bbc5b65a44f19f018b1141e85371 +dist/2024-07-26/rust-std-beta-i686-unknown-linux-gnu.tar.gz=748dfafeb6b953017b9129be9697d02a73d5bc8b1371a6a00bbfa20b7b45033c +dist/2024-07-26/rust-std-beta-i686-unknown-linux-gnu.tar.xz=0c9a0717054858fa821e4c418b9e99cb0a0f852ec3c866951c1d1795cc1e8152 +dist/2024-07-26/rust-std-beta-i686-unknown-linux-musl.tar.gz=96afb9b33c86a6007f9c02f1f418d28f13014ec5154b648ceb7f725449dcbddf +dist/2024-07-26/rust-std-beta-i686-unknown-linux-musl.tar.xz=7ed52c039fdd13a7477ed211ad8432cfd2fab98832174c8a534e5ac280f28572 +dist/2024-07-26/rust-std-beta-i686-unknown-uefi.tar.gz=676786bd5e7f0739d18d40d99158045fcba746dc306485c0f15ce49ef2b63b0b +dist/2024-07-26/rust-std-beta-i686-unknown-uefi.tar.xz=d7fdba095ca515454034891c8a862aaa563ee7451289138ce32b10691dbfe38a +dist/2024-07-26/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=ff2eb11f284da7883dbf6ae465af86e50a9cd98c9b7d22b1e710626716e07db5 +dist/2024-07-26/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=953fedb9dac336cb796290f619919f4ff9b2a6fcce09e23072f93a40b5498bdd +dist/2024-07-26/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=ba1b4f772faf2e738d2696eb576f74bdbd1fed1a073053eee341ec6423b6e1c6 +dist/2024-07-26/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=f3f3eaafd9ecb1ee0ba68259d88d65ced56f20323a4f542fc85993e92a044206 +dist/2024-07-26/rust-std-beta-loongarch64-unknown-none.tar.gz=f20ecabfab9d8ecbab44d661018b865e449137993726e76b20c7749db4d0986e +dist/2024-07-26/rust-std-beta-loongarch64-unknown-none.tar.xz=7da2cf413c0665b07b3083862747c9815a78b3775c2bee6cd1f7551e7f2e4c8a +dist/2024-07-26/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=3f7529ea5dcb17c18efa949916dd1ebc29b123a265cc8839cee4fe4e637f19be +dist/2024-07-26/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=d2a30f8659cc66d48da0e6c1919707943321d7f3ac0e2d58a9078aec16c86ef7 +dist/2024-07-26/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=f971daa3f8c3d0773fb6667cb7a544f96ebd049e9b5d1b5ac4bd3ea0c342467b +dist/2024-07-26/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=3cd2f4ad7d9f41e9f903aa49eb86d142bb5ef69c2c39d9ba16a00a39d74046f1 +dist/2024-07-26/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=b319ee87ee034a6a2a67dc2efdc72f8285c73d9bd1bd893586170ac54049151a +dist/2024-07-26/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=c751e65bbdfd72de07abf138162ab64dcbce51ba50375cef5ca8f35945d9ac7b +dist/2024-07-26/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=0228f8d7e0913cca5db975e0b9ec4807e817dcbc0cd0bd64b64826c9537f25e5 +dist/2024-07-26/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=d37d5ae3605d745a393fc71c5f0e169cee283aa840edb9543682b339cd040310 +dist/2024-07-26/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=6dd97a7215bdc8145cee5856957bf3f20719f65c73033006243ac2a9e347394b +dist/2024-07-26/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=d7541a8e83f7b8a9761988c3ac295a665e21e4d5dc9e2284ef48314f6c784166 +dist/2024-07-26/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=34b21aec2cc1b92b2911211fe3c798918ea6c99d51975f6a979c2da3a09b0a05 +dist/2024-07-26/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=50ba48e76e60ffbfb2bb0ba0b8e12418b7f6a9d7cde31b508bb4656536934e82 +dist/2024-07-26/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=e84526736c1a48dbec3b122b8c01458ff549afdbdd0f73e705c76cca1bd1f767 +dist/2024-07-26/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=df8d9909b0afd0e02b9c027f12e28aa2c40716d7c8a8f5e82b7521d2e145fbf8 +dist/2024-07-26/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=7b671c5257a7653481fe6bc090c55398607590292590e0fdf5973f5f9b7b18b1 +dist/2024-07-26/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=7f4d1a8e757fd29f4f58005032608df709003098eb661fe98a0b3116b7237e6a +dist/2024-07-26/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=9d957edfba201b541bd589ca4085e26bf6f890b3970817fe9af6ccf5f2eb0854 +dist/2024-07-26/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=0f867e1391009d1d0caefb6b9217fc8e9f3e087a7dd9b22a157719bdc44ac074 +dist/2024-07-26/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=a02ccac46574fad8929b419eff7510b8a773211d3ccce9ca96f0485e2dab8eaa +dist/2024-07-26/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=b70f37cddd519d0d207e28089b469f702bfe72ba5d32a631c85270db75b83d61 +dist/2024-07-26/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=151863cece818be834261e6c148eeabbb1c796c991065c84b813bac64e73fef7 +dist/2024-07-26/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=94cbae695036a4a1fa1faac12a6dd5ebeea18ec2d6e7b01c311d340ce7ddba39 +dist/2024-07-26/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=3c8ca657208a4d96eff193363375a33d966dc319cd329dd7a0d379e052ab43bc +dist/2024-07-26/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=037f7b599421eca888332dadd797509d81af04743eac06120a9882b27aff5ca2 +dist/2024-07-26/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=d931a3611003cbe61003ca4feeb637449f1d10f7969cd27ca37f11cf5536e465 +dist/2024-07-26/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=3c7a3a9e1e03c2fe097e2e7353d00d02e864d3964db559f6a08a3d33f1fb7927 +dist/2024-07-26/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=f877200e388345c10d3b0f5b8c484dd42905e769dcc1e21692e1cf7f2d59317e +dist/2024-07-26/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=1052e8e5681c95ed3c004adee2492ff19d109965e9441a3362df876719899486 +dist/2024-07-26/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=e487752db0ecd837fb1ecb57064ef2464f7f85ed5fdc9d47322e7b5c37c32300 +dist/2024-07-26/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=e74a462d598c55822b5841bdc35cb2605b4aa7993e305246b3adf079ae79ea1d +dist/2024-07-26/rust-std-beta-sparcv9-sun-solaris.tar.gz=3ea692ffe99c953d0049c0a16b71eb0bc3da492b9f9308463311dc0143af84dd +dist/2024-07-26/rust-std-beta-sparcv9-sun-solaris.tar.xz=97005f0d686a6481c5a532b6e1ea14befc794ed89394a8f1c94ad39257ebdaa8 +dist/2024-07-26/rust-std-beta-thumbv6m-none-eabi.tar.gz=2aa967414bd03764774338e377c4714b8d169b1bbc651624a20158441b8111c3 +dist/2024-07-26/rust-std-beta-thumbv6m-none-eabi.tar.xz=d1a3b6ccbc55d1d9555674d1596a0c9f489304f9cb211e0c4645c17256ada4d1 +dist/2024-07-26/rust-std-beta-thumbv7em-none-eabi.tar.gz=33fe3c61aa2e93c6a925b132fabc11c24e102cc070260d5f89a66e374e9a4d23 +dist/2024-07-26/rust-std-beta-thumbv7em-none-eabi.tar.xz=f50bb682fe484f23ae1ae9b2466c450b8fe9ecc3694fb68e162ce43cc8917af7 +dist/2024-07-26/rust-std-beta-thumbv7em-none-eabihf.tar.gz=53a17d10d498f1b6ebd6852987f304b2e1187544f2c9e91b69d0fdbf88630910 +dist/2024-07-26/rust-std-beta-thumbv7em-none-eabihf.tar.xz=fb97e6f2b047d6251eb6e34891b64f20d0dbc648beb78bb7364fe563670d7de8 +dist/2024-07-26/rust-std-beta-thumbv7m-none-eabi.tar.gz=940eef037faceb2b9bdb1094eac6b175843094d3a3ab3c9850eb28742643e92d +dist/2024-07-26/rust-std-beta-thumbv7m-none-eabi.tar.xz=c892b8a80db788d1f4bf64ec6f740bb1168a8b9dc88d105366892a476ad3c244 +dist/2024-07-26/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=905d378e6fd4387381fc166718ff31f017c99e725a9c8461bbac39d5571e246f +dist/2024-07-26/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=6c5fc29004bda36ddfb0280636ac2b2c28c538f33cadb07a55174fadb068daa5 +dist/2024-07-26/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=38c8b07558734a9fff1807a823af72faaf8ab5ea47042f1b88a38d6c3648c43b +dist/2024-07-26/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=e30e0000336449a8aabf60839cba4ecca32cf82b02ed0a7a23935793072679f4 +dist/2024-07-26/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=db784556ebf56a79e6330de3204ce6ce5422eca2543b872ed55c238aedbfedae +dist/2024-07-26/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=373efe8c55e85aae60dfac234471b76fefbc2ba722d97e0338ec593b7c70cce3 +dist/2024-07-26/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=1bd5a11163f5fb9325e21c425d5d2fb3e1f845d265cbf7736679c3bb9c8d22a7 +dist/2024-07-26/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=d858d0077df0fa7363258994e76a1bc0ff850734de6a0fbe955c3c9b9cd46cb7 +dist/2024-07-26/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=07b356cd0ec3f18617e8f5be04d784847acfca43d6fa2101821dcdee18aada36 +dist/2024-07-26/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=aecacd11945e6e053fe27faab2c0816628b392cb06c41feb8ed693d43043eba4 +dist/2024-07-26/rust-std-beta-wasm32-unknown-emscripten.tar.gz=7bf6bdb443503d48cb711f8b3e2670fe20e02f71ae940c2f1b273ab1d15452b7 +dist/2024-07-26/rust-std-beta-wasm32-unknown-emscripten.tar.xz=9ed34d7467722660d625e9a048fcb843bdb80aac407b0395a693a5698f6c1465 +dist/2024-07-26/rust-std-beta-wasm32-unknown-unknown.tar.gz=5144fe563c42205ba777678291950bd1b8c2b010b9aae28e2fe96e14e93e28cb +dist/2024-07-26/rust-std-beta-wasm32-unknown-unknown.tar.xz=6e55e79f65a02abf37e1533c2590f53dfe56b474b58a3220d88f3d1458b6dcdc +dist/2024-07-26/rust-std-beta-wasm32-wasi.tar.gz=84d03b1c1fc7338895f82f54fdccb18ea6c4ceb43f47c3d46fadb014aa0c9549 +dist/2024-07-26/rust-std-beta-wasm32-wasi.tar.xz=f28cbf9552e3c680342364087e7305a12417a92ed6b6696d71d476e1c1d8d4e7 +dist/2024-07-26/rust-std-beta-wasm32-wasip1.tar.gz=8d56f1aa8661047f854806278b83a514fa79b7a5097635cd095943b3f848ea74 +dist/2024-07-26/rust-std-beta-wasm32-wasip1.tar.xz=78609204d01e0d0bcbc1fb5dd3d9cfcac60db23bfbd2428f1e021d1b05793519 +dist/2024-07-26/rust-std-beta-wasm32-wasip1-threads.tar.gz=ed1b9141bd68b8c13e295904ac66d18ca92c5b92a263fa5109262d15065aeded +dist/2024-07-26/rust-std-beta-wasm32-wasip1-threads.tar.xz=0c036271c4c4e04357b630146cad296dc6a57363f7dc0cd2f3142ead13bf0602 +dist/2024-07-26/rust-std-beta-x86_64-apple-darwin.tar.gz=c12bc5543c262645f7000cc84247518033f15fcbdfed510eb92393d723906eb1 +dist/2024-07-26/rust-std-beta-x86_64-apple-darwin.tar.xz=a5fc26dd6068cdb28ee2a252af545a34dd2be6e075e7d0aac2d03796c07579f4 +dist/2024-07-26/rust-std-beta-x86_64-apple-ios.tar.gz=a62be7f900cec101b1b7444c7466e8fe0b823d708fadd92bce8bd531866b4938 +dist/2024-07-26/rust-std-beta-x86_64-apple-ios.tar.xz=993875efe27c1018bbcb8326665247a33eda6fc0e934f71124b0a08d72685474 +dist/2024-07-26/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=bc6ae4d5e93b47828678f1af55efc1baae2d76975be3259854272a566f5489c9 +dist/2024-07-26/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=51cf1d675e61aaeedf895e52a9a81b188aa066a68d599963e0435bddfd2f6a5b +dist/2024-07-26/rust-std-beta-x86_64-linux-android.tar.gz=6bd4089f30f05d9902d667a2d48028345bd558e7365bd3957bc554a4d0c1f939 +dist/2024-07-26/rust-std-beta-x86_64-linux-android.tar.xz=2a7e9250e3bea7f7e502131012139eadb66c71e95e8cec1698e22b21e7c9fbcd +dist/2024-07-26/rust-std-beta-x86_64-pc-solaris.tar.gz=4169c938b638faff18a5db1bed5e7d2036f45d25ec9799dc2967ad1589d447a9 +dist/2024-07-26/rust-std-beta-x86_64-pc-solaris.tar.xz=8cbda8eeb98be575b78170e9895390e9406c4de41db8363988185dd24e276427 +dist/2024-07-26/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=99a14bf8cffa0c736a25ce117bc5d4ab64963d81c9d9ac377f118a77a6a3c4b6 +dist/2024-07-26/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=9902878b75b3f6cf944909cf6d17efc26dc820708b164b34757d841e89c948bd +dist/2024-07-26/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=54b83918128c3d19b09a4e1801783500266794395e6a6f138ad647bd890b79f6 +dist/2024-07-26/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=2b0ed80c922e25f3783fbc0ce181b76b7f758d2a1b85b823851b7ac35c6872df +dist/2024-07-26/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=44ce7faad791029b670a9d815766a647d488dbbb56eb09024a09a595427fd19b +dist/2024-07-26/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=cd7011512df191100d31caef920d1a5c6d5ec2a771e0b52078d5d60b45e9a497 +dist/2024-07-26/rust-std-beta-x86_64-unknown-freebsd.tar.gz=dc20c2fc14b2211bf4517464109871f940a227298302d2ec84eaca539a19e9b4 +dist/2024-07-26/rust-std-beta-x86_64-unknown-freebsd.tar.xz=5a7fa24ca3f7b43526c116a5ebce60f29116640ad51c1ed426f9aee705b10897 +dist/2024-07-26/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=975917e862347f7e1472cdaa2258de16cb9981e3a8256f9b7860104740076187 +dist/2024-07-26/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=bb89062b05f025a964e0110fcfc8fdf57300b23314409924cd4a05e9af9614af +dist/2024-07-26/rust-std-beta-x86_64-unknown-illumos.tar.gz=a1c1e3e15bea1c4e6c70b8a56c4fef1c231b6ab5528c32cb69ded38456b3fa21 +dist/2024-07-26/rust-std-beta-x86_64-unknown-illumos.tar.xz=43b31f7dd50621faff96c13a2246c80e84fc4da4076d4aedca4e9a6bf63ca43b +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=3f8df0e35e2e8d5e0f8181f20f0639323aa8e8c21da60697599447c0d77f783a +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=d0e727d9176abdbef02b1276b3b1acf69068b9c0fc0477fb3248fd06c99292b5 +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=73ba835c5c4c9fcabbcb4e1460d402cf0c0aa9b2e01342521897abd9769dcc02 +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=64e6fc64ccfd0adb2fabc610eba6e877ab83f2de49532363deef4579728baf6f +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=58dcb73ba02fa38742d255bd346a77340cf634247904624c7af9b11e769a364c +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=ea4417de121ab7e869659b26efd144baf2fd839a956da4acb30121d1bc9132aa +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=d1e861a9176a5cb934270592f7b98528283c363d135736477a844499015ac44a +dist/2024-07-26/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=c5f548845c6ccbee1980892e630ab06ec1bef498fb7f6fc80b4ced4a84c23f85 +dist/2024-07-26/rust-std-beta-x86_64-unknown-netbsd.tar.gz=3f6677ecdb1a2f016e9941234c221b06fd0a21cd9bae83a59bdcfde055c4878a +dist/2024-07-26/rust-std-beta-x86_64-unknown-netbsd.tar.xz=60eae08756d92a70b740ecd8a1412138a9a14486289d73440bf7ee068586f9c4 +dist/2024-07-26/rust-std-beta-x86_64-unknown-none.tar.gz=1f11ad1a66d01a09c7237da76327b70ae2e2a9f7647a3ee0de58d563576efacf +dist/2024-07-26/rust-std-beta-x86_64-unknown-none.tar.xz=960fb614a927e1b3d1d8e4a9fb7220ae675d0e8219fdde204bc6454fbe69a0a9 +dist/2024-07-26/rust-std-beta-x86_64-unknown-redox.tar.gz=3af955fc52feaee2d89d6d50e0df4d00417897679f0d607a0f146a8d34c20496 +dist/2024-07-26/rust-std-beta-x86_64-unknown-redox.tar.xz=cb607387d0524bc84610cd6cc0a0070e0dadc379a7fb1a736f3f336cf0458b8d +dist/2024-07-26/rust-std-beta-x86_64-unknown-uefi.tar.gz=ae56b334234ba51cf68be42d0fd58cf3487673dbe747bb3efa01e8563b9b8a90 +dist/2024-07-26/rust-std-beta-x86_64-unknown-uefi.tar.xz=1468622e9bb6384a6ab7006863d2b7d55307e74239befc8e72dfd39f83c0af1e +dist/2024-07-26/cargo-beta-aarch64-apple-darwin.tar.gz=62f687da4c02bef3e114b92c75b62fc6f7fdfcd210fb66329f6d4780f7dbcd70 +dist/2024-07-26/cargo-beta-aarch64-apple-darwin.tar.xz=f9346e9ed4e5638f58dc237b22aef9e1b25b23bb588c736dceaa25b7b935f2bb +dist/2024-07-26/cargo-beta-aarch64-pc-windows-msvc.tar.gz=f2442eab2908bb90c551f56da8889e39ce44a31936ce63e13a54432105a67cf9 +dist/2024-07-26/cargo-beta-aarch64-pc-windows-msvc.tar.xz=ffc8c803668d09333fdfee996fa72c8874e91d92567f0b4eff9077d7b9a3070a +dist/2024-07-26/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=97f6b771f7e559df03f00f8665c6580535499d4afa7bf2a252a145b1aa0e2c0b +dist/2024-07-26/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=3ee58d5c57ba00e27428392dab1d69655bf7e997a0d3aa6629bb32f4f863396b +dist/2024-07-26/cargo-beta-aarch64-unknown-linux-musl.tar.gz=b36871d3c88eca54e4c24d50171fd649d10a92f689d4812ebef7b510f0c3d799 +dist/2024-07-26/cargo-beta-aarch64-unknown-linux-musl.tar.xz=ee550d60ae217fe7aec12feada1ba1f84222427677de42382d192ec748f73f18 +dist/2024-07-26/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=6599270c877164e76ef30f00a9b47da1a021fdaf1d4f7fdb194500f861ca8d21 +dist/2024-07-26/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=46429528039b5499c6ac4618c4b73fcdfabf7fdd5ed9f9f3d64ccde0428d78c0 +dist/2024-07-26/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=01ba37e80e18e673c275538fc0db63b667ef52a72000dcbe76ac68018bd585e8 +dist/2024-07-26/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=004f5119454fa53c2a7a58f6d176f65903b3c5bfc8eb1f894f6ca4e9bc32b7a1 +dist/2024-07-26/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=2bb2e69563684bf64607d3f584b4216b24fe190dd2eabd6c00c2044290beabde +dist/2024-07-26/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=454b0dafb3cd62788dc541ddfea5b484ac799253937602f6456e853c2a293707 +dist/2024-07-26/cargo-beta-i686-pc-windows-gnu.tar.gz=779d704f8f64a7290227631d1e396fe00bdada3cfb7f3e6c775a0bb00047cb97 +dist/2024-07-26/cargo-beta-i686-pc-windows-gnu.tar.xz=8cce8e286ff04f4d58235e8716a0ca05aedfeb9db6fb8952c523264ea649e542 +dist/2024-07-26/cargo-beta-i686-pc-windows-msvc.tar.gz=58b31a781da4f5215b0fbb5f355cbc3c5b2212971c68a1e00de19b851698fda3 +dist/2024-07-26/cargo-beta-i686-pc-windows-msvc.tar.xz=008125eca7351844707c9c14637030da45441a68d74f6e2752ee713f6403f03b +dist/2024-07-26/cargo-beta-i686-unknown-linux-gnu.tar.gz=cbfc580db37b135a20f42f9da02517ec8cf0a286b7274252c23c6a4947ba6755 +dist/2024-07-26/cargo-beta-i686-unknown-linux-gnu.tar.xz=dc2198fabb6fe6f68353c268698dece4ca3d6901b424a7cc2c68850f81f75f20 +dist/2024-07-26/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=46ea726328e1700cf2b7d0ddeb16e25ebb648267c76268ba2f4aecb75ba47661 +dist/2024-07-26/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=520ee25c1f18d80b4e4ce25e8fd961a19c0dd2ee0274e9d9cd3201f7c57c6f77 +dist/2024-07-26/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=fb710b6ae058619042bdf80ef5c2a56457076692a122b4155e0a53f382e8ad83 +dist/2024-07-26/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=ac15e80c28c7a4570bb4a99fa70522a3ae5ba602ce25e96b8e82d097f8b42904 +dist/2024-07-26/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=6907a40c1e7b18146ff2a60c6d44c9716f31cd4aeaae4fe858032aa26c359d27 +dist/2024-07-26/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=4287d73ae2b3272a1893c2ffa32d007edf0b05ff2dbf1da92aa20f2b857e0eab +dist/2024-07-26/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=edbdd5fc1e9bc5fdea67e7c2fda3fa9f3eb3f599c307a278bc7bf45ff97d1f48 +dist/2024-07-26/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=9c7d487f65a6273b76970b2c057b8120abbd89ba67a2f92dd446ecbacf218576 +dist/2024-07-26/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=74e57cbc139d548202d94262170ee45e33ae01331358ac2eaa9fa67c968ea1ea +dist/2024-07-26/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=6cf99b689e32ff0caa56e9704dd185f6c268b484ccb657eff212e66fe64d1c10 +dist/2024-07-26/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=ed13a39d74e9d814d585ce7fb1aafd093f94317d6ead76e4b43c03b01e210086 +dist/2024-07-26/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=b5379d677d6596ffcbd7f3ebef2fd6d968096228101104a45565b170fb050042 +dist/2024-07-26/cargo-beta-s390x-unknown-linux-gnu.tar.gz=1a29502881e6512a8bea566a90781870181f5727359668fa3bed2b3210a21b8d +dist/2024-07-26/cargo-beta-s390x-unknown-linux-gnu.tar.xz=95970245a7f304c6b83b8dad58b4c877ad2c9b0ca6bfc0090151561ce53a217b +dist/2024-07-26/cargo-beta-x86_64-apple-darwin.tar.gz=521f03a968c6e0640de6f09ecbc6328ea57b2815f353a14e1d4dd09925e98366 +dist/2024-07-26/cargo-beta-x86_64-apple-darwin.tar.xz=14b733585cc768d26a822de630d990f519b6d6b4ae079f09bacd51b7449c18a4 +dist/2024-07-26/cargo-beta-x86_64-pc-windows-gnu.tar.gz=1bf5d0201ca87a76de8044dd8222517bd130ef3db631dc60a84e2fceced465d7 +dist/2024-07-26/cargo-beta-x86_64-pc-windows-gnu.tar.xz=2eaeb6863c655047e6c938e208ae952df4da2d8b4326aeda60918414764aa7b6 +dist/2024-07-26/cargo-beta-x86_64-pc-windows-msvc.tar.gz=7b7ef80aa6131ddcb8bfec0f9493417e9a1e617a8f599a27de52ee914d022f95 +dist/2024-07-26/cargo-beta-x86_64-pc-windows-msvc.tar.xz=647a6d0ff8f38dd4404c0006e5ed7b3a981dc89bbc334b1bd53225cc4f5511c8 +dist/2024-07-26/cargo-beta-x86_64-unknown-freebsd.tar.gz=bcd0e100d4c66784a2357be74813551c68d4e3b5e6846bfd1bbcb316e5c82c53 +dist/2024-07-26/cargo-beta-x86_64-unknown-freebsd.tar.xz=cc31bb9f5a67359f4af4743afbdf9b694e40cd209211d4ea819c94183de6ab60 +dist/2024-07-26/cargo-beta-x86_64-unknown-illumos.tar.gz=12f6a8d3d17b54ea260f6f5b19be9e208111b0ad37518ae6b6eae558d77a1dc5 +dist/2024-07-26/cargo-beta-x86_64-unknown-illumos.tar.xz=221c9cf9d1fce2b5c50bcf8f435f5335f1c5e9533f51d52b941593d6a964b4c8 +dist/2024-07-26/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=53c64845abe4582a90041b2128b3f931ce636d01fc9a8509b35ded5dd7ae0a35 +dist/2024-07-26/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=61805cfca6e7f602eaab0fde2be48cfed244105b2f4b5bff798752ce06d62713 +dist/2024-07-26/cargo-beta-x86_64-unknown-linux-musl.tar.gz=92ac04de9689176a42e5f0ccf1e44ce78b64bcd15d6e570c171d51c44cf8c6ce +dist/2024-07-26/cargo-beta-x86_64-unknown-linux-musl.tar.xz=59a371de4343d330b79e84f629c98b5cb9dc58bd9706d1a7a9ff474b28394851 +dist/2024-07-26/cargo-beta-x86_64-unknown-netbsd.tar.gz=f48bb7b7d13862edaa8949be046b5bd2b8c63b0aab8e29bffb1940ec149eb123 +dist/2024-07-26/cargo-beta-x86_64-unknown-netbsd.tar.xz=0a6e09604fe291d0a9a0845a8ac7f434c9f4ffbb698c042ba83a070c23f804fb +dist/2024-07-26/clippy-beta-aarch64-apple-darwin.tar.gz=4c8991c692f200c6a5f25957e6648e07b7271681cbd9c61749bc990216dbbe6f +dist/2024-07-26/clippy-beta-aarch64-apple-darwin.tar.xz=51b5b85d46af2a3faeb53b81472c285eccc01f0c83575f62f37739cd48397fe2 +dist/2024-07-26/clippy-beta-aarch64-pc-windows-msvc.tar.gz=3a7755c10b32ee3b4f95875c974f21efad2be7ae27d317bbac056b3e5b9298b7 +dist/2024-07-26/clippy-beta-aarch64-pc-windows-msvc.tar.xz=718692e6a1e00ca042d9767273e487c941c65440e64729844a46e42ec6a8abb2 +dist/2024-07-26/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=18da4a1fac25597186bdc5c825c71bc6fe020e8e6552ac97dd307a0311acab20 +dist/2024-07-26/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=86a7ec123586e0c953efd23b82aea5eab62ab8c065bf3596d3001a23b01243ce +dist/2024-07-26/clippy-beta-aarch64-unknown-linux-musl.tar.gz=267b8dd0b5024152e36bcfb15af0b8ae3c68a71f559b8ecea1a3c9a8982e315b +dist/2024-07-26/clippy-beta-aarch64-unknown-linux-musl.tar.xz=6aaae715599da14ca8cc405e51deed3bc160661ec56e929ae347ef1ead7ccf00 +dist/2024-07-26/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=f794412df97e19240f628037f5d1d7391dd09ee1a6de02f4dbff72e2b03d94b6 +dist/2024-07-26/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=49a78a14b9638cabd19ba4261f5bd04066646e1260146e3be288182b73fa1864 +dist/2024-07-26/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=3dcd9775b46a100a50fbb00188dffdc6f07fca9843994c8406a2d053e41c1672 +dist/2024-07-26/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=e069b852ace430b2f75486a291d3d7849439c67c3381f5eedc0d6a9e6cde56db +dist/2024-07-26/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=57f7109bef934d0da06f7aea6eaca72b0e66f82d1160fbae05f53c2276da2b82 +dist/2024-07-26/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=5ed50d8d088ae0d86ba51a03bf56040da027376f70b5114f593813daa4ce7a40 +dist/2024-07-26/clippy-beta-i686-pc-windows-gnu.tar.gz=d8712ca2ab26256c0fade4ecc8dd8e4ea6770d91e16e460ff2fe3f070febc59f +dist/2024-07-26/clippy-beta-i686-pc-windows-gnu.tar.xz=9d0c88881e0535f9cb4a48f59b389b1c2a0b5fa46ad8bfa37fbc540676a8f557 +dist/2024-07-26/clippy-beta-i686-pc-windows-msvc.tar.gz=ffa017562648e98c7229e43a7422602c265441228485f0e14e52ebaf48a13e91 +dist/2024-07-26/clippy-beta-i686-pc-windows-msvc.tar.xz=292ab3d318a34e67463728752dc175f65cf5530f9368344dccaa79bad4df3b85 +dist/2024-07-26/clippy-beta-i686-unknown-linux-gnu.tar.gz=89bd927bc9370a405561f6534c5cd88e0f6e06b0b512589e5722349b12a17763 +dist/2024-07-26/clippy-beta-i686-unknown-linux-gnu.tar.xz=732120c4fcdde08fadca139216a91af7a6abee15f0a41735dc8860b36039d88d +dist/2024-07-26/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=101e3118bf23246195b159abab59ee5626e767977d97f00e71fc5e67dfbf7784 +dist/2024-07-26/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=e40c0c15a7b195ed03c5e1167c2cb5e765ef643b0f6621c36643faf87bf46369 +dist/2024-07-26/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=f6ed6391c8997f942d180cd32e81aca654450fb050cd865a8a1b13f77a892245 +dist/2024-07-26/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=16e736f65b0ff029fe9fbafceec938123dff1b5a133f6c48b25edab379a20685 +dist/2024-07-26/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=711fdb406a23ab5c3b1199e7af06608297bd071d7e48734a03bbc1f7a00b66dd +dist/2024-07-26/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=36d05ba29927b56e0d815dd09b39a3db12b6395e9717e653c20449fa83aa636f +dist/2024-07-26/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=d53fb59c723a2e76f1716cf1c82aacd2627e1694aebf70511c24529a4fe1ee99 +dist/2024-07-26/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=ffec66667d95cabc893cc87988218552eba8930f031419612f80144dcf2cc881 +dist/2024-07-26/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=a2cd11651304ecf5ce6a1ee5fdd1acbfd36b1dbd2680cd76f2b9d3a86012b837 +dist/2024-07-26/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=c8a8d9fe8badb1e06811412a15ce9cdfef07ecc59bf83cd578b33889751bb560 +dist/2024-07-26/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=01ba50dae501edf3afa60aab59d8d81ccfd91126ded4cf7d45f7c4b13fb8c4aa +dist/2024-07-26/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=a7e749bc581f9c64137a00dd891c4abff0d7f5684d2d501aad20ed1b01d9a312 +dist/2024-07-26/clippy-beta-s390x-unknown-linux-gnu.tar.gz=f19081755c37e3dc43091b06bcdc59c82b28c6fa0a78ff02d800a25d6d9f22f2 +dist/2024-07-26/clippy-beta-s390x-unknown-linux-gnu.tar.xz=d5f222e23f4f0ed4929e11684bc7b861c29fa183fffba0dbbfd5739b921f5234 +dist/2024-07-26/clippy-beta-x86_64-apple-darwin.tar.gz=edd631b7938dae6bb6c2694df0dab3ff6670e1a5ab3b5f0e6ce5bd7e992b6310 +dist/2024-07-26/clippy-beta-x86_64-apple-darwin.tar.xz=e4bc44ff13cfdd70f94c5efe86e93de041599e570c19611141c00700a656b40c +dist/2024-07-26/clippy-beta-x86_64-pc-windows-gnu.tar.gz=1b1aff83773de4dd972734cb03209eccfc3793f442a1a5dea51f451ca34cd1cc +dist/2024-07-26/clippy-beta-x86_64-pc-windows-gnu.tar.xz=71c357ff694cd954314c47e3bd458465453c65e6022799827b8560ecae16fdc8 +dist/2024-07-26/clippy-beta-x86_64-pc-windows-msvc.tar.gz=d4759993b9659ad8354d3fdf858cf99efebdf8202442505e11b55d24a54b31a0 +dist/2024-07-26/clippy-beta-x86_64-pc-windows-msvc.tar.xz=e29eaea7b7f2e54b9285a2216f5b85440313650090c268048ce9e830e007fa92 +dist/2024-07-26/clippy-beta-x86_64-unknown-freebsd.tar.gz=4009c19d557156e63ad7123d27264650d046cd20e723ec6cc8ffdb3fc1d3b8b1 +dist/2024-07-26/clippy-beta-x86_64-unknown-freebsd.tar.xz=9622117651423c53c35f69cb3087a5294cca098ac93aeb7c18bcab035a1e69ef +dist/2024-07-26/clippy-beta-x86_64-unknown-illumos.tar.gz=a88fd4b3e30faaecf270936422e254a25ff5dba93b08ca4003a9978ee80d0838 +dist/2024-07-26/clippy-beta-x86_64-unknown-illumos.tar.xz=f391b26009b7089f3d49d2f3409bb4e02e3de0700284727d2d26e7062bb18011 +dist/2024-07-26/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=e467501c65651d519ae9a489e821e7d5dc82fdbeb2da2d4c75f093e1b898abd6 +dist/2024-07-26/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=9dcbd07899af86db3c101275ab0f2fd3b5b0c43d4a92c5b5be05f0e4500d7535 +dist/2024-07-26/clippy-beta-x86_64-unknown-linux-musl.tar.gz=c2a30c78a6ab64d1655651fdd5c7f970dadcd6dfd1a585d130bd890190fe67f6 +dist/2024-07-26/clippy-beta-x86_64-unknown-linux-musl.tar.xz=05c2a38fa5bca162e365218670a7c195ebd9b3491d14464282a273dfe93b0ad7 +dist/2024-07-26/clippy-beta-x86_64-unknown-netbsd.tar.gz=75ee727a80119a810193519535f962721d547297feb9b5597b397e59277de701 +dist/2024-07-26/clippy-beta-x86_64-unknown-netbsd.tar.xz=002b726868a14df5ea31fe8eb75fae24660fb1cfad73591e86f9179dd1bb2dc4 +dist/2024-07-30/rustfmt-nightly-aarch64-apple-darwin.tar.gz=d3bb2f1fa20555373189caa7e58a8e3071c7ea6e7059bab15d6dd52dfef56c4f +dist/2024-07-30/rustfmt-nightly-aarch64-apple-darwin.tar.xz=ba0019eb0766dd438069988156994270959c875b010afce0f455540a0bb2ee2f +dist/2024-07-30/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=24bbbe80a998b78f04c15d82fcef9498315b1b8d0ddabeeb5b6729b14bafbf70 +dist/2024-07-30/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=cc396d3639c7213ab6442dc79ab6a1a42bb17910aa6d27046cc89ebeb6973231 +dist/2024-07-30/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=e6dc772727a20920607923ff5a99526054e6244c40cc6c97fe68db58896de6fc +dist/2024-07-30/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=eec95e09ec7bd64ab8a47e7311103f5bc58aa3499edcba052699686315f8441a +dist/2024-07-30/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=fef3eb7d5dee7458daacfba9b97d579868864267fcdc85334f02384683ce0c17 +dist/2024-07-30/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=44f376f4d9e84645373b4cbfe17678c288a71b92622ab4091ca7b82ed6a21b57 +dist/2024-07-30/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=5c17927f12149fa605e3f671d25ac456d1d2bdac796b5ca466bfa7fb8688d224 +dist/2024-07-30/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=04783b1a2efc10696b0d32fde7c63437e17548d38b2272f928f28915107f4f18 +dist/2024-07-30/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=1365d93efc4391f189726e69c7a968c8debb50411f29404da42d19bc9e053937 +dist/2024-07-30/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=db344c9f6d31ac5a2efc839cbb886c312534e9b1fe4b116629eaca74f79c316d +dist/2024-07-30/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=049a2b666956a56365661d944a0fc030673bec5578062ddaad104363f4ff1031 +dist/2024-07-30/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=32d3bc08316c0cfc3c1ea689ccdc80978f744864c048863b9d51ae59b6e2f0c2 +dist/2024-07-30/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=bc33833cd41bf5ac36730fc9d3ef9c87643d42b8e711de286138962fb5fac4a5 +dist/2024-07-30/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=feffb4527ad627a0239d788272edac9a0fbec704de3a67aabbd8a9cbb95d4fe3 +dist/2024-07-30/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=0ffb98b839f6640ac75e094cbb469a06f2e45f51ced01efbeb51a3fba866a048 +dist/2024-07-30/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=d229ea866bf4d6c2d8d748630146c7bccedb4ba5a17056fbdc4558baf976f067 +dist/2024-07-30/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=431de55efb797c75d8e440ba3da9ac70a04cccfc56b4e22b80b6488360915f90 +dist/2024-07-30/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=e9e4eac4205014b6b61c759a8326f7a5e44e8ff05726510a8bbefd656f78e97c +dist/2024-07-30/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=99381507f8fc42d9bd00c545dd7ef4734e364a4e994d02bae148f78b94f50ab5 +dist/2024-07-30/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=6e2cd283d9370beb9c208bd823ac2433df500adf7e70d8acfe5a261e62be2b4a +dist/2024-07-30/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=761a9733f53ecba343139bd1c028a66242824ef8f7c54ae63e0aaf0e77cabcf9 +dist/2024-07-30/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=c2973a99220588ea78b7a052861c345f01254073d6bb77e96982b12382907e2b +dist/2024-07-30/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=4288ee8fc5f650b3d5c24be2ea114e133d3bad8ddf7e2211cdb72151410c755a +dist/2024-07-30/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=2a31e1f211a26f6798acd4c779827bfa4e1ac4d77103a48be74617e5af1661a1 +dist/2024-07-30/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=9598edeb1ff5eff934f1e4783b3ed263babb53d3513564ccccf453dada872ba1 +dist/2024-07-30/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=eb5392fce8ee4a016406efba546366ff91d4db8cda94dd131dbda70c553b6c06 +dist/2024-07-30/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=0e9c55ee790eed653eda7b913e64d344ccbbe1681321377186d1e4882dc93bfc +dist/2024-07-30/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=c9a0b3eddac707c4e0c1cdc754d2821845e5f4bdcbd001b71d7287fe851cc44b +dist/2024-07-30/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=99c264f806d4a6a1288a9cc7f28e178102520ebe4728fb0da3f8142c1d6720e0 +dist/2024-07-30/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=3f1835460dbf17a3dbf47f2772e182ebf6337fd6a946a033f9700fb38140eda6 +dist/2024-07-30/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=385224b95c56de8598dd5b4eeb48d7d7a8eb920825c55bb4d7be54142d7e2857 +dist/2024-07-30/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=535220608ee858d46746c8de4c1e58f4f675061517b6b06c8af47004d5291f85 +dist/2024-07-30/rustfmt-nightly-x86_64-apple-darwin.tar.gz=aa19f1856befff101bcb433049e923486d7bc1a056638e478651936572e7e3bf +dist/2024-07-30/rustfmt-nightly-x86_64-apple-darwin.tar.xz=2ed04cf9fddeafbb83d82988e00d3b7f5ae9382468ce1093cbb110bf46b04711 +dist/2024-07-30/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=962dea60de3ac68a573f088f9c1b003816ce93f9088df15fe8cf7771a3abdc73 +dist/2024-07-30/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=72f7b787644bd9008b13018512c0b09a425c15a337821dc9bded9ab7211c61de +dist/2024-07-30/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=1096ba25a23461eed84804584eb9ffdff780e2c737cc9dce1e8929419dfd645b +dist/2024-07-30/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=854b9e410e65b953ab71f356bd5b618679aa7c1f5a3de7f81ec388720ed04f60 +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=03b4ab410c70fee3daa7a577c311b29344158a7d35ea87627233cb6165191338 +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=d755700cc17416c4488db2d582250b5ec329fc0c2b94e9db0e6688ca30a7316c +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=b7448089c61c2f0a427416cd7521130c31a98c52e435bd0d04d6aad72d765ba6 +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=cb3f479897032ff9ec5e06913e8e4374ce46af7f8a9030299c7b0cab2dab2005 +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=0c7dbff79d04218a7ba3f5992f67d25043bc5f348038f37ab7385cbc4d0df00d +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=4f62cbc75a81d57748111a915ebb20f561f403c1b695141780d50c398b186fa6 +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=e5e8b07ae7ba0e1f526828a10f2515a68b0ed6023eed25e3bb10b80e21e8db76 +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=424ec90c59fca7e21b302efd7c6c7f7dbc5dcadd1afbb15e90efea38ea8a19cf +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=f22576893113958f6caabcf3ab3e38287fea010c1809a8bb1197220d6660ea4e +dist/2024-07-30/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=5e85141abe2a3c537cb4171c60cff5bdb11f075aa79212e0a958081d4648aec6 +dist/2024-07-30/rustc-nightly-aarch64-apple-darwin.tar.gz=f793e4645be9461fae09e82710bcfdfc6d749735be202f2e2cd6c2c643bb8925 +dist/2024-07-30/rustc-nightly-aarch64-apple-darwin.tar.xz=e42f52cc5a865ab92321b97aaeff4d39cbaad1a5b60a9a77de9f42e3b3231d21 +dist/2024-07-30/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=0258ba5bf957d190ef3a384c5451ebbe8406c195a29b3a7ee2a4c48e0514f203 +dist/2024-07-30/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=788b7e9239562f43c0d96cfd66f543604eef1c9b40d22898c456fade608e9c31 +dist/2024-07-30/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=94187df22d7b96a5c39c5c9a078f566b80d392abfd2a5d7059b836cfa0c446e1 +dist/2024-07-30/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=ca1381c6de1a96f32a30736c28aa196f7e653b4f57f01ffe2db52fe238a9f202 +dist/2024-07-30/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=1c72596862a133003f68590a8ae09bf6ab25a5f9667a6d245eafede1ebd8f450 +dist/2024-07-30/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=de0c216f229301bc88690b10b8f063b8e389915503980973b91bfe17b1fb6eda +dist/2024-07-30/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=e00283a7ee9a702446456feb0156f1d1fc738fd013983f15c456fcafb8294309 +dist/2024-07-30/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=03fab128bc3756676c645f637edc5051f8096b00780d8ef4de7a85b3f248933a +dist/2024-07-30/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=105e06c0ca69b4a700980f2a588f831c23a9a9253268bd652110328b99918cb1 +dist/2024-07-30/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=0da27ffe5504a03923ae5858b69cdd6eb276e6bd2bfb504bcf13ebdda654316b +dist/2024-07-30/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=edb1bec402f3799e05fd3e31f30763e7266d2e062e410af8eab7636440da272c +dist/2024-07-30/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=638a09ecfe0b66391ce6802e3e15bf15462aca1c8cfb2a49b948ec69eda0575c +dist/2024-07-30/rustc-nightly-i686-pc-windows-gnu.tar.gz=456683fa10703ba8249f8b1735293c79edac0c32aec60c1a91315fb41c8e5ecc +dist/2024-07-30/rustc-nightly-i686-pc-windows-gnu.tar.xz=fd5d9a7bf543d3c61ca85340961d5e17b42b37c35fe45c666c2a0e125548f42e +dist/2024-07-30/rustc-nightly-i686-pc-windows-msvc.tar.gz=2b75a4e15d1f98f123ef74aecab3570296aeb7e35f18548a2b3d1735e01b06e0 +dist/2024-07-30/rustc-nightly-i686-pc-windows-msvc.tar.xz=1163178e95180725bd31b7dabf1e9a9788a8e7cc576dd53148cfec32443e64a1 +dist/2024-07-30/rustc-nightly-i686-unknown-linux-gnu.tar.gz=0aad8dd79beca26278e1c1847dde17ee22ae64b78f2b02be65e12bc126de699b +dist/2024-07-30/rustc-nightly-i686-unknown-linux-gnu.tar.xz=a548e90fbce90c26b6700cff7964d72c7e959a6f317e891db4f91ce7cd187c41 +dist/2024-07-30/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=654ec9040afb20d660603adcd31824efeacef0ae2d48d342bb9504bd2f723453 +dist/2024-07-30/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=998407bc1303eecb1ef4c6b14862ffdf31fb0cf991f5d169a822a23627b29352 +dist/2024-07-30/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=5b4febf9a1802c2c756ef8a7693a9cfd83bf95284d79d9f9b9dd265234d6aebc +dist/2024-07-30/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=dbc2a6417fabcfb37f4fa42d50391c1cb29f18d1771321088c08afdf0a0e4809 +dist/2024-07-30/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=2befcab4aa3e31064b87e4696aa2f1e76bd9c24067eb1921feb7f6497d58338c +dist/2024-07-30/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=508a3c1241507a53a0d6b27df2171eaf0a4df903773b5592f005ee4d39d91287 +dist/2024-07-30/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=c4e8da248306be350f7a89b2522e976a12ab89d5ae2e19157ed3b148e3948c50 +dist/2024-07-30/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=5858e1cf6371f25ba3c381e07ba6f4551c2a13516011fc10dbdafc4caf1bff93 +dist/2024-07-30/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=3639bbb7aac353772171dcfaecd589352de3fd8cc7395e7628d7590830034af4 +dist/2024-07-30/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=3cad5f0b74fc3f5be70cf496c93054c50e8a3f92cfc894a2febfbecc382dcb55 +dist/2024-07-30/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=437fc8b815c09a3c5159735d9fec0e5d8dd64416149cb77c13a16863e6636412 +dist/2024-07-30/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=7bc2917e7816f7df1bc7faeda28010d247c923aba412b5ec1cc9c6757eec37d9 +dist/2024-07-30/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=603e391a14c26a51f4f02020b5abbd307d29476fdc0689e5bf9570f2e6ac69a8 +dist/2024-07-30/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=d04ea4e522cf1ecaa6b2f3edcb32155142378bc648aa159a574838ed2d58b794 +dist/2024-07-30/rustc-nightly-x86_64-apple-darwin.tar.gz=0ed639da01e58645eb33a75e020d57c58c92651d817e89574cb67bae5b298d35 +dist/2024-07-30/rustc-nightly-x86_64-apple-darwin.tar.xz=b4010f38396e7b8acd67cf2234c94ede11cf78a226a134c809d504e594037311 +dist/2024-07-30/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=7ec8aba7a7581d59c7ceaffdd374852d08b17bc7e02674c5bfd4f47f4a0aee39 +dist/2024-07-30/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=41c3ba0543220e11bc53282dd7d2a37acd6450a59683e1c83d2150dc798cfba7 +dist/2024-07-30/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=67e5d75dda47c587a4be517db547354d1da0228110b7d5488d8822a7ef7cda20 +dist/2024-07-30/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=4b5f0c4101a783dd313c0cd1435d7eccfeac97452afaeaf42ba975805541dc74 +dist/2024-07-30/rustc-nightly-x86_64-unknown-freebsd.tar.gz=09e12b5003969b768b5c06b3e3b41a64429e28c32790ff28c12b8c5814f350c0 +dist/2024-07-30/rustc-nightly-x86_64-unknown-freebsd.tar.xz=a4b3b402cf4beb3e7c7b9b6620f34d045ef42261514ae7d8483d3790d1dfd88a +dist/2024-07-30/rustc-nightly-x86_64-unknown-illumos.tar.gz=c344d0c95aadac0e5e0100743b80768c9d0cd76b123ab07e6387ad1ea3e3efba +dist/2024-07-30/rustc-nightly-x86_64-unknown-illumos.tar.xz=ac0a79660227fe163c796e6b816596299f6d8b32ba008ce30cde492709efb7c0 +dist/2024-07-30/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=4090ff8086317662d3def1bc92d51e6f2decd2c64f4b6ede38604fd5029d3d02 +dist/2024-07-30/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=e665bd4e3763b456576eaa45bcf188648cc6e6443ecdff8ef91a5c7649cbfd25 +dist/2024-07-30/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=4fa994e08274599ae3bceb9310c584be89d11f231aad3e8fc1e2d6a6ddbb77bf +dist/2024-07-30/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=bd8d53403bf6337782bffc3050a46887fbe2568c850809c5b32c8d84492d2ee9 +dist/2024-07-30/rustc-nightly-x86_64-unknown-netbsd.tar.gz=606a430036fdda0b92dd4a4cb99830881389693e6ed02a01ddd51eaecdfdf463 +dist/2024-07-30/rustc-nightly-x86_64-unknown-netbsd.tar.xz=fa34273b20336b8b373f686c88ee297033f635895ad225a2ca5271b6da3d7dea \ No newline at end of file diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index ff475b9571b87..2b263f848e89e 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -141,6 +141,7 @@ static TARGETS: &[&str] = &[ "riscv64gc-unknown-hermit", "riscv64gc-unknown-none-elf", "riscv64gc-unknown-linux-gnu", + "riscv64gc-unknown-linux-musl", "s390x-unknown-linux-gnu", "sparc64-unknown-linux-gnu", "sparcv9-sun-solaris", diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index fed225f2f2dc6..f8c75ca722609 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -1,7 +1,5 @@ #![deny(unused_variables)] -use std::collections::HashMap; - use anyhow::{Context, Error}; use build_helper::stage0_parser::{parse_stage0_file, Stage0Config, VersionMetadata}; use curl::easy::Easy; @@ -217,13 +215,13 @@ enum Channel { #[derive(Debug, serde::Serialize, serde::Deserialize)] struct Manifest { date: String, - pkg: HashMap, + pkg: IndexMap, } #[derive(Debug, serde::Serialize, serde::Deserialize)] struct ManifestPackage { version: String, - target: HashMap, + target: IndexMap, } #[derive(Debug, serde::Serialize, serde::Deserialize)] diff --git a/src/tools/cargo b/src/tools/cargo index b5d44db1daf04..94977cb1fab00 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit b5d44db1daf0469b227a6211b987162a39a54730 +Subproject commit 94977cb1fab003d45eb5bb108cb5e2fa0149672a diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 22d23a175094a..a388b6b2eaab3 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -8,7 +8,6 @@ #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![feature(never_type)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index e4e7f7d06e706..080efe983c2a8 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -145,7 +145,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ( "this function's return value is unnecessary".to_string(), "remove the return type...".to_string(), - snippet(cx, fn_decl.output.span(), "..").to_string(), + // FIXME: we should instead get the span including the `->` and suggest an + // empty string for this case. + "()".to_string(), "...and then remove returned values", ) } else { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 3a9714c49ae73..1d5f1a2a2bb13 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -5,7 +5,6 @@ #![feature(f16)] #![feature(if_let_guard)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![feature(never_type)] #![feature(rustc_private)] #![feature(assert_matches)] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 9b1577f24b8e7..3fafe2427a256 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -2,7 +2,6 @@ #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index c069b9adf2d96..58729a97d57bb 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -1,6 +1,7 @@ #![warn(clippy::uninit_vec)] use std::mem::MaybeUninit; +use std::cell::UnsafeCell; #[derive(Default)] struct MyVec { @@ -12,6 +13,12 @@ union MyOwnMaybeUninit { uninit: (), } +// https://github.com/rust-lang/rust/issues/119620 +unsafe fn requires_paramenv() { + let mut vec = Vec::>::with_capacity(1); + vec.set_len(1); +} + fn main() { // with_capacity() -> set_len() should be detected let mut vec: Vec = Vec::with_capacity(1000); diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 8e93466c2cc84..e8b77d653f089 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -1,5 +1,17 @@ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:17:5 + --> tests/ui/uninit_vec.rs:18:5 + | +LL | let mut vec = Vec::>::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + = note: `-D clippy::uninit-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:24:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,11 +20,9 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ | = help: initialize the buffer or wrap the content in `MaybeUninit` - = note: `-D clippy::uninit-vec` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::uninit_vec)]` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:24:5 + --> tests/ui/uninit_vec.rs:31:5 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -23,7 +33,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:31:5 + --> tests/ui/uninit_vec.rs:38:5 | LL | let mut vec: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +42,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:38:5 + --> tests/ui/uninit_vec.rs:45:5 | LL | let mut vec: Vec = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +51,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` on empty `Vec` creates out-of-bound values - --> tests/ui/uninit_vec.rs:44:5 + --> tests/ui/uninit_vec.rs:51:5 | LL | let mut vec: Vec = Vec::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +60,7 @@ LL | vec.set_len(200); | ^^^^^^^^^^^^^^^^ error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:61:5 + --> tests/ui/uninit_vec.rs:68:5 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +71,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:71:5 + --> tests/ui/uninit_vec.rs:78:5 | LL | my_vec.vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +82,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:77:5 + --> tests/ui/uninit_vec.rs:84:5 | LL | my_vec.vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +93,7 @@ LL | my_vec.vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:52:9 + --> tests/ui/uninit_vec.rs:59:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +104,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:56:9 + --> tests/ui/uninit_vec.rs:63:9 | LL | vec.reserve(1000); | ^^^^^^^^^^^^^^^^^^ @@ -105,7 +115,7 @@ LL | vec.set_len(200); = help: initialize the buffer or wrap the content in `MaybeUninit` error: calling `set_len()` immediately after reserving a buffer creates uninitialized values - --> tests/ui/uninit_vec.rs:132:9 + --> tests/ui/uninit_vec.rs:139:9 | LL | let mut vec: Vec = Vec::with_capacity(1000); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -115,5 +125,5 @@ LL | vec.set_len(10); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr index 15708090361ed..37ee9195fce66 100644 --- a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr @@ -63,7 +63,7 @@ LL | let _val = None::<()>.expect("this always happens"); help: remove the `None` and `expect()` | LL | let _val = panic!("this always happens"); - | ~~~~~~~ ~ + | ~~~~~~~ error: used `unwrap_or_default()` on `None` value --> tests/ui/unnecessary_literal_unwrap.rs:22:24 @@ -134,7 +134,7 @@ LL | None::<()>.expect("this always happens"); help: remove the `None` and `expect()` | LL | panic!("this always happens"); - | ~~~~~~~ ~ + | ~~~~~~~ error: used `unwrap_or_default()` on `None` value --> tests/ui/unnecessary_literal_unwrap.rs:30:5 diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr index a55a23d449f57..59986d895b30e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr @@ -118,8 +118,8 @@ LL | | } | help: remove the return type... | -LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { - | ~~~~~~~~~~ +LL | fn issue_6640_1(a: bool, b: bool) -> () { + | ~~ help: ...and then remove returned values | LL ~ return ; @@ -145,8 +145,8 @@ LL | | } | help: remove the return type... | -LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { - | ~~~~~~~~~~~~~~~ +LL | fn issue_6640_2(a: bool, b: bool) -> () { + | ~~ help: ...and then remove returned values | LL ~ return ; diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index c356f4266f016..0706f3bee05c1 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -18,6 +18,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "check-test-line-numbers-match", "compare-output-lines-by-subset", "compile-flags", + "doc-flags", "dont-check-compiler-stderr", "dont-check-compiler-stdout", "dont-check-failure-status", @@ -117,6 +118,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-watchos", "ignore-windows", "ignore-windows-gnu", + "ignore-windows-msvc", "ignore-x32", "ignore-x86", "ignore-x86_64", @@ -225,6 +227,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "should-ice", "stderr-per-bitwidth", "test-mir-pass", + "unique-doc-out-dir", "unset-exec-env", "unset-rustc-env", // Used by the tidy check `unknown_revision`. diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 0208ed34ac182..1fc24301c85e6 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -95,6 +95,8 @@ pub struct TestProps { pub compile_flags: Vec, // Extra flags to pass when the compiled code is run (such as --bench) pub run_flags: Vec, + /// Extra flags to pass to rustdoc but not the compiler. + pub doc_flags: Vec, // If present, the name of a file that this test should match when // pretty-printed pub pp_exact: Option, @@ -122,6 +124,9 @@ pub struct TestProps { pub unset_exec_env: Vec, // Build documentation for all specified aux-builds as well pub build_aux_docs: bool, + /// Build the documentation for each crate in a unique output directory. + /// Uses /docs//doc + pub unique_doc_out_dir: bool, // Flag to force a crate to be built with the host architecture pub force_host: bool, // Check stdout for error-pattern output as well as stderr @@ -220,8 +225,10 @@ mod directives { pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern"; pub const COMPILE_FLAGS: &'static str = "compile-flags"; pub const RUN_FLAGS: &'static str = "run-flags"; + pub const DOC_FLAGS: &'static str = "doc-flags"; pub const SHOULD_ICE: &'static str = "should-ice"; pub const BUILD_AUX_DOCS: &'static str = "build-aux-docs"; + pub const UNIQUE_DOC_OUT_DIR: &'static str = "unique-doc-out-dir"; pub const FORCE_HOST: &'static str = "force-host"; pub const CHECK_STDOUT: &'static str = "check-stdout"; pub const CHECK_RUN_RESULTS: &'static str = "check-run-results"; @@ -267,6 +274,7 @@ impl TestProps { regex_error_patterns: vec![], compile_flags: vec![], run_flags: vec![], + doc_flags: vec![], pp_exact: None, aux_builds: vec![], aux_bins: vec![], @@ -281,6 +289,7 @@ impl TestProps { exec_env: vec![], unset_exec_env: vec![], build_aux_docs: false, + unique_doc_out_dir: false, force_host: false, check_stdout: false, check_run_results: false, @@ -330,6 +339,7 @@ impl TestProps { pub fn from_file(testfile: &Path, revision: Option<&str>, config: &Config) -> Self { let mut props = TestProps::new(); props.load_from(testfile, revision, config); + props.exec_env.push(("RUSTC".to_string(), config.rustc_path.display().to_string())); match (props.pass_mode, props.fail_mode) { (None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check), @@ -377,6 +387,8 @@ impl TestProps { |r| r, ); + config.push_name_value_directive(ln, DOC_FLAGS, &mut self.doc_flags, |r| r); + fn split_flags(flags: &str) -> Vec { // Individual flags can be single-quoted to preserve spaces; see // . @@ -414,6 +426,8 @@ impl TestProps { config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice); config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs); + config.set_name_directive(ln, UNIQUE_DOC_OUT_DIR, &mut self.unique_doc_out_dir); + config.set_name_directive(ln, FORCE_HOST, &mut self.force_host); config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout); config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 1f15605d8beed..b1b6d6fc8eb18 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1,5 +1,6 @@ // ignore-tidy-filelength +use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::ffi::{OsStr, OsString}; use std::fs::{self, create_dir_all, File, OpenOptions}; @@ -723,7 +724,7 @@ impl<'test> TestCx<'test> { self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags); rustc.args(&self.props.compile_flags); - self.compose_and_run_compiler(rustc, Some(src)) + self.compose_and_run_compiler(rustc, Some(src), self.testpaths) } fn run_debuginfo_test(&self) { @@ -1579,13 +1580,15 @@ impl<'test> TestCx<'test> { passes, ); - self.compose_and_run_compiler(rustc, None) + self.compose_and_run_compiler(rustc, None, self.testpaths) } - fn document(&self, out_dir: &Path) -> ProcRes { + /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run. + /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths. + fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes { if self.props.build_aux_docs { for rel_ab in &self.props.aux_builds { - let aux_testpaths = self.compute_aux_test_paths(&self.testpaths, rel_ab); + let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab); let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config); let aux_cx = TestCx { @@ -1596,7 +1599,9 @@ impl<'test> TestCx<'test> { }; // Create the directory for the stdout/stderr files. create_dir_all(aux_cx.output_base_dir()).unwrap(); - let auxres = aux_cx.document(out_dir); + // use root_testpaths here, because aux-builds should have the + // same --out-dir and auxiliary directory. + let auxres = aux_cx.document(&root_out_dir, root_testpaths); if !auxres.status.success() { return auxres; } @@ -1606,21 +1611,40 @@ impl<'test> TestCx<'test> { let aux_dir = self.aux_output_dir_name(); let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed"); - let mut rustdoc = Command::new(rustdoc_path); + // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire + // test + let out_dir: Cow<'_, Path> = if self.props.unique_doc_out_dir { + let file_name = self.testpaths.file.file_stem().expect("file name should not be empty"); + let out_dir = PathBuf::from_iter([ + root_out_dir, + Path::new("docs"), + Path::new(file_name), + Path::new("doc"), + ]); + create_dir_all(&out_dir).unwrap(); + Cow::Owned(out_dir) + } else { + Cow::Borrowed(root_out_dir) + }; + + let mut rustdoc = Command::new(rustdoc_path); + let current_dir = output_base_dir(self.config, root_testpaths, self.safe_revision()); + rustdoc.current_dir(current_dir); rustdoc .arg("-L") .arg(self.config.run_lib_path.to_str().unwrap()) .arg("-L") .arg(aux_dir) .arg("-o") - .arg(out_dir) + .arg(out_dir.as_ref()) .arg("--deny") .arg("warnings") .arg(&self.testpaths.file) .arg("-A") .arg("internal_features") - .args(&self.props.compile_flags); + .args(&self.props.compile_flags) + .args(&self.props.doc_flags); if self.config.mode == RustdocJson { rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options"); @@ -1630,7 +1654,7 @@ impl<'test> TestCx<'test> { rustdoc.arg(format!("-Clinker={}", linker)); } - self.compose_and_run_compiler(rustdoc, None) + self.compose_and_run_compiler(rustdoc, None, root_testpaths) } fn exec_compiled_test(&self) -> ProcRes { @@ -1828,9 +1852,16 @@ impl<'test> TestCx<'test> { } } - fn compose_and_run_compiler(&self, mut rustc: Command, input: Option) -> ProcRes { + /// `root_testpaths` refers to the path of the original test. + /// the auxiliary and the test with an aux-build have the same `root_testpaths`. + fn compose_and_run_compiler( + &self, + mut rustc: Command, + input: Option, + root_testpaths: &TestPaths, + ) -> ProcRes { let aux_dir = self.aux_output_dir(); - self.build_all_auxiliary(&self.testpaths, &aux_dir, &mut rustc); + self.build_all_auxiliary(root_testpaths, &aux_dir, &mut rustc); rustc.envs(self.props.rustc_env.clone()); self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove); @@ -2545,7 +2576,7 @@ impl<'test> TestCx<'test> { Vec::new(), ); - let proc_res = self.compose_and_run_compiler(rustc, None); + let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths); let output_path = self.get_filecheck_file("ll"); (proc_res, output_path) } @@ -2581,7 +2612,7 @@ impl<'test> TestCx<'test> { Vec::new(), ); - let proc_res = self.compose_and_run_compiler(rustc, None); + let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths); let output_path = self.get_filecheck_file("s"); (proc_res, output_path) } @@ -2664,7 +2695,7 @@ impl<'test> TestCx<'test> { let out_dir = self.output_base_dir(); remove_and_create_dir_all(&out_dir); - let proc_res = self.document(&out_dir); + let proc_res = self.document(&out_dir, &self.testpaths); if !proc_res.status.success() { self.fatal_proc_rec("rustdoc failed!", &proc_res); } @@ -2723,7 +2754,7 @@ impl<'test> TestCx<'test> { let aux_dir = new_rustdoc.aux_output_dir(); new_rustdoc.build_all_auxiliary(&new_rustdoc.testpaths, &aux_dir, &mut rustc); - let proc_res = new_rustdoc.document(&compare_dir); + let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths); if !proc_res.status.success() { eprintln!("failed to run nightly rustdoc"); return; @@ -2847,7 +2878,7 @@ impl<'test> TestCx<'test> { let out_dir = self.output_base_dir(); remove_and_create_dir_all(&out_dir); - let proc_res = self.document(&out_dir); + let proc_res = self.document(&out_dir, &self.testpaths); if !proc_res.status.success() { self.fatal_proc_rec("rustdoc failed!", &proc_res); } @@ -2923,31 +2954,24 @@ impl<'test> TestCx<'test> { fn check_rustdoc_test_option(&self, res: ProcRes) { let mut other_files = Vec::new(); let mut files: HashMap> = HashMap::new(); - let cwd = env::current_dir().unwrap(); - files.insert( - self.testpaths - .file - .strip_prefix(&cwd) - .unwrap_or(&self.testpaths.file) - .to_str() - .unwrap() - .replace('\\', "/"), - self.get_lines(&self.testpaths.file, Some(&mut other_files)), - ); + let normalized = fs::canonicalize(&self.testpaths.file).expect("failed to canonicalize"); + let normalized = normalized.to_str().unwrap().replace('\\', "/"); + files.insert(normalized, self.get_lines(&self.testpaths.file, Some(&mut other_files))); for other_file in other_files { let mut path = self.testpaths.file.clone(); path.set_file_name(&format!("{}.rs", other_file)); - files.insert( - path.strip_prefix(&cwd).unwrap_or(&path).to_str().unwrap().replace('\\', "/"), - self.get_lines(&path, None), - ); + let path = fs::canonicalize(path).expect("failed to canonicalize"); + let normalized = path.to_str().unwrap().replace('\\', "/"); + files.insert(normalized, self.get_lines(&path, None)); } let mut tested = 0; for _ in res.stdout.split('\n').filter(|s| s.starts_with("test ")).inspect(|s| { if let Some((left, right)) = s.split_once(" - ") { let path = left.rsplit("test ").next().unwrap(); - if let Some(ref mut v) = files.get_mut(&path.replace('\\', "/")) { + let path = fs::canonicalize(&path).expect("failed to canonicalize"); + let path = path.to_str().unwrap().replace('\\', "/"); + if let Some(ref mut v) = files.get_mut(&path) { tested += 1; let mut iter = right.split("(line "); iter.next(); @@ -3779,7 +3803,7 @@ impl<'test> TestCx<'test> { if let Some(nodejs) = &self.config.nodejs { let out_dir = self.output_base_dir(); - self.document(&out_dir); + self.document(&out_dir, &self.testpaths); let root = self.config.find_rust_src_root().unwrap(); let file_stem = @@ -4095,7 +4119,7 @@ impl<'test> TestCx<'test> { rustc.arg(crate_name); } - let res = self.compose_and_run_compiler(rustc, None); + let res = self.compose_and_run_compiler(rustc, None, self.testpaths); if !res.status.success() { self.fatal_proc_rec("failed to compile fixed code", &res); } diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 6ee147da5a965..05191a159801c 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -191,7 +191,7 @@ impl<'test> TestCx<'test> { rustdoc_cmd.arg(&self.testpaths.file); - let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None); + let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None, self.testpaths); if !proc_res.status.success() { self.fatal_proc_rec("rustdoc --test failed!", &proc_res) } diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 525de03bbce33..7d6ec475badd4 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -13,7 +13,6 @@ pub(crate) enum Kind { Variant, Function, TypeAlias, - OpaqueTy, Constant, Trait, TraitAlias, @@ -55,7 +54,6 @@ impl Kind { // FIXME(adotinthevoid): I'm not sure if these are correct Keyword => false, - OpaqueTy => false, ProcAttribute => false, ProcDerive => false, @@ -99,7 +97,6 @@ impl Kind { Kind::Enum => false, Kind::Variant => false, Kind::TypeAlias => false, - Kind::OpaqueTy => false, Kind::Constant => false, Kind::Trait => false, Kind::TraitAlias => false, @@ -149,7 +146,6 @@ impl Kind { ItemEnum::TraitAlias(_) => TraitAlias, ItemEnum::Impl(_) => Impl, ItemEnum::TypeAlias(_) => TypeAlias, - ItemEnum::OpaqueTy(_) => OpaqueTy, ItemEnum::Constant { .. } => Constant, ItemEnum::Static(_) => Static, ItemEnum::Macro(_) => Macro, @@ -177,7 +173,6 @@ impl Kind { ItemKind::Keyword => Keyword, ItemKind::Macro => Macro, ItemKind::Module => Module, - ItemKind::OpaqueTy => OpaqueTy, ItemKind::Primitive => Primitive, ItemKind::ProcAttribute => ProcAttribute, ItemKind::ProcDerive => ProcDerive, diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 6d790c52174e3..0ffb96bef29ba 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -3,9 +3,9 @@ use std::hash::Hash; use rustdoc_json_types::{ Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs, - GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, - OpaqueTy, Path, Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, - Type, TypeAlias, TypeBinding, TypeBindingKind, Union, Variant, VariantKind, WherePredicate, + GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, Path, + Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeAlias, + TypeBinding, TypeBindingKind, Union, Variant, VariantKind, WherePredicate, }; use serde_json::Value; @@ -101,7 +101,6 @@ impl<'a> Validator<'a> { ItemEnum::TraitAlias(x) => self.check_trait_alias(x), ItemEnum::Impl(x) => self.check_impl(x, id), ItemEnum::TypeAlias(x) => self.check_type_alias(x), - ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), ItemEnum::Constant { type_, const_ } => { self.check_type(type_); self.check_constant(const_); @@ -230,11 +229,6 @@ impl<'a> Validator<'a> { self.check_type(&x.type_); } - fn check_opaque_ty(&mut self, x: &'a OpaqueTy) { - x.bounds.iter().for_each(|b| self.check_generic_bound(b)); - self.check_generics(&x.generics); - } - fn check_constant(&mut self, _x: &'a Constant) { // nop } diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 417ecb09b3196..e4bfd9bd12282 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" @@ -85,26 +85,32 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", "regex-automata", "serde", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -134,9 +140,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" [[package]] name = "cfg-if" @@ -258,18 +264,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -320,9 +326,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -356,9 +362,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -401,9 +407,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -426,9 +432,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "levenshtein" @@ -438,9 +444,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libffi" @@ -463,12 +469,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -483,9 +489,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -499,9 +505,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "measureme" @@ -519,9 +525,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -534,9 +540,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -563,6 +569,7 @@ dependencies = [ "smallvec", "tempfile", "ui_test", + "windows-sys 0.52.0", ] [[package]] @@ -630,9 +637,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -648,7 +655,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -715,15 +722,18 @@ checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettydiff" @@ -737,9 +747,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -785,9 +795,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] @@ -805,9 +815,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -817,9 +827,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -828,15 +838,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -880,9 +890,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -892,27 +902,27 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.200" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -921,11 +931,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -953,9 +964,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -964,30 +975,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -1086,9 +1098,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "valuable" @@ -1098,9 +1110,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1145,7 +1157,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1165,18 +1177,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1187,9 +1199,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1199,9 +1211,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1211,15 +1223,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1229,9 +1241,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1241,9 +1253,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1253,9 +1265,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1265,9 +1277,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yansi-term" @@ -1277,3 +1289,24 @@ checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" dependencies = [ "winapi", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 976bd080867eb..e12f3f9012f0d 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -9,12 +9,12 @@ default-run = "miri" edition = "2021" [lib] -test = true # we have unit tests +test = true # we have unit tests doctest = false # but no doc tests [[bin]] name = "miri" -test = false # we have no unit tests +test = false # we have no unit tests doctest = false # and no doc tests [dependencies] @@ -42,6 +42,13 @@ libc = "0.2" libffi = "3.2.0" libloading = "0.8" +[target.'cfg(target_family = "windows")'.dependencies] +windows-sys = { version = "0.52", features = [ + "Win32_Foundation", + "Win32_System_IO", + "Win32_Storage_FileSystem", +] } + [dev-dependencies] colored = "2" ui_test = "0.21.1" diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index b1be596c00679..499d0fe2f1c93 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -398,6 +398,8 @@ to Miri failing to detect cases of undefined behavior in a program. application instead of raising an error within the context of Miri (and halting execution). Note that code might not expect these operations to ever panic, so this flag can lead to strange (mis)behavior. +* `-Zmiri-recursive-validation` is a *highly experimental* flag that makes validity checking + recurse below references. * `-Zmiri-retag-fields[=]` controls when Stacked Borrows retagging recurses into fields. `all` means it always recurses (the default, and equivalent to `-Zmiri-retag-fields` without an explicit value), `none` means it never recurses, `scalar` means it only recurses for @@ -474,6 +476,19 @@ Miri provides some `extern` functions that programs can import to access Miri-specific functionality. They are declared in [/tests/utils/miri\_extern.rs](/tests/utils/miri_extern.rs). +## Entry point for no-std binaries + +Binaries that do not use the standard library are expected to declare a function like this so that +Miri knows where it is supposed to start execution: + +```rust +#[cfg(miri)] +#[no_mangle] +fn miri_start(argc: isize, argv: *const *const u8) -> isize { + // Call the actual start function that your project implements, based on your target's conventions. +} +``` + ## Contributing and getting help If you want to contribute to Miri, great! Please check out our diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index 8bd8f1030532e..7369a3fbf09ae 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -4,21 +4,21 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -104,9 +104,9 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -121,9 +121,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libredox" @@ -137,9 +137,21 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "option-ext" @@ -149,9 +161,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -178,9 +190,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb" +checksum = "2471f8f296262437d7e848e527b4210b44a96e53a3b4435b890227ce3e6da106" dependencies = [ "anyhow", "rustc_version", @@ -218,9 +230,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -233,27 +245,27 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.200" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -262,20 +274,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "syn" -version = "2.0.60" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -284,30 +297,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -338,11 +352,11 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -360,7 +374,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -380,18 +403,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -402,9 +425,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -414,9 +437,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -426,15 +449,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -444,9 +467,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -456,9 +479,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -468,9 +491,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -480,6 +503,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index 6acdbc46f642a..477c60db162a3 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "5" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.18.0" -rustc-build-sysroot = "0.5.2" +rustc-build-sysroot = "0.5.3" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index fe67aad465cdd..0cf6f1a375cdf 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -100,7 +100,10 @@ pub fn setup( // for target crates. let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); if env::var_os("RUSTC_STAGE").is_some() { - assert!(env::var_os("RUSTC").is_some()); + assert!( + env::var_os("RUSTC").is_some() && env::var_os("RUSTC_WRAPPER").is_some(), + "cargo-miri setup is running inside rustc bootstrap but RUSTC or RUST_WRAPPER is not set" + ); command.env("RUSTC_REAL", &cargo_miri_path); } else { command.env("RUSTC", &cargo_miri_path); diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 62a3ab2c34cf5..fc205040baf57 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -355,7 +355,10 @@ impl Command { let head = cmd!(sh, "git rev-parse HEAD").read()?; let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; if head != fetch_head { - bail!("Josh created a non-roundtrip push! Do NOT merge this into rustc!"); + bail!( + "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ + Expected {head}, got {fetch_head}." + ); } println!( "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:" diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 58ec4e10856df..b74f9759ebed6 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a526d7ce45fd2284e0e7c7556ccba2425b9d25e5 +29e924841f06bb181d87494eba2783761bc1ddec diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index d0f977f81433f..ed955e78c3e9a 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -11,7 +11,7 @@ use rand::Rng; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; -use rustc_target::abi::{Align, HasDataLayout, Size}; +use rustc_target::abi::{Align, Size}; use crate::{concurrency::VClock, *}; @@ -105,15 +105,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Returns the exposed `AllocId` that corresponds to the specified addr, // or `None` if the addr is out of bounds - fn alloc_id_from_addr(&self, addr: u64) -> Option { + fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option { let ecx = self.eval_context_ref(); let global_state = ecx.machine.alloc_addresses.borrow(); assert!(global_state.provenance_mode != ProvenanceMode::Strict); + // We always search the allocation to the right of this address. So if the size is structly + // negative, we have to search for `addr-1` instead. + let addr = if size >= 0 { addr } else { addr.saturating_sub(1) }; let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr); // Determine the in-bounds provenance for this pointer. - // (This is only called on an actual access, so in-bounds is the only possible kind of provenance.) let alloc_id = match pos { Ok(pos) => Some(global_state.int_to_ptr_map[pos].1), Err(0) => None, @@ -305,20 +307,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance) let alloc_id = prov.alloc_id(); - let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?; - // Add offset with the right kind of pointer-overflowing arithmetic. - let dl = ecx.data_layout(); - let absolute_addr = dl.overflowing_offset(base_addr, offset.bytes()).0; - Ok(interpret::Pointer::new( + // Get a pointer to the beginning of this allocation. + let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?; + let base_ptr = interpret::Pointer::new( Provenance::Concrete { alloc_id, tag }, - Size::from_bytes(absolute_addr), - )) + Size::from_bytes(base_addr), + ); + // Add offset with the right kind of pointer-overflowing arithmetic. + Ok(base_ptr.wrapping_offset(offset, ecx)) } /// When a pointer is used for a memory access, this computes where in which allocation the /// access is going. - fn ptr_get_alloc(&self, ptr: interpret::Pointer) -> Option<(AllocId, Size)> { + fn ptr_get_alloc( + &self, + ptr: interpret::Pointer, + size: i64, + ) -> Option<(AllocId, Size)> { let ecx = self.eval_context_ref(); let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance) @@ -327,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { alloc_id } else { // A wildcard pointer. - ecx.alloc_id_from_addr(addr.bytes())? + ecx.alloc_id_from_addr(addr.bytes(), size)? }; // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer @@ -335,12 +341,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let base_addr = *ecx.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap(); // Wrapping "addr - base_addr" - #[allow(clippy::cast_possible_wrap)] // we want to wrap here - let neg_base_addr = (base_addr as i64).wrapping_neg(); - Some(( - alloc_id, - Size::from_bytes(ecx.overflowing_signed_offset(addr.bytes(), neg_base_addr).0), - )) + let rel_offset = ecx.truncate_to_target_usize(addr.bytes().wrapping_sub(base_addr)); + Some((alloc_id, Size::from_bytes(rel_offset))) } } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 25b154a8206c9..e13e54c33094a 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -14,11 +14,14 @@ extern crate tracing; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_metadata; extern crate rustc_middle; extern crate rustc_session; +extern crate rustc_span; +extern crate rustc_target; use std::env::{self, VarError}; use std::num::NonZero; @@ -29,7 +32,9 @@ use tracing::debug; use rustc_data_structures::sync::Lrc; use rustc_driver::Compilation; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{self as hir, Node}; +use rustc_hir_analysis::check::check_function_signature; use rustc_interface::interface::Config; use rustc_middle::{ middle::{ @@ -37,14 +42,17 @@ use rustc_middle::{ exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel}, }, query::LocalCrate, - ty::TyCtxt, + traits::{ObligationCause, ObligationCauseCode}, + ty::{self, Ty, TyCtxt}, util::Providers, }; -use rustc_session::config::{CrateType, ErrorOutputType, OptLevel}; +use rustc_session::config::{CrateType, EntryFnType, ErrorOutputType, OptLevel}; use rustc_session::search_paths::PathKind; use rustc_session::{CtfeBacktrace, EarlyDiagCtxt}; +use rustc_span::def_id::DefId; +use rustc_target::spec::abi::Abi; -use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields}; +use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields, ValidationMode}; struct MiriCompilerCalls { miri_config: miri::MiriConfig, @@ -82,11 +90,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { tcx.dcx().fatal("miri only makes sense on bin crates"); } - let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) { - entry_def - } else { - tcx.dcx().fatal("miri can only run programs that have a main function"); - }; + let (entry_def_id, entry_type) = entry_fn(tcx); let mut config = self.miri_config.clone(); // Add filename to `miri` arguments. @@ -351,6 +355,56 @@ fn jemalloc_magic() { } } +fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, EntryFnType) { + if let Some(entry_def) = tcx.entry_fn(()) { + return entry_def; + } + // Look for a symbol in the local crate named `miri_start`, and treat that as the entry point. + let sym = tcx.exported_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| { + if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None } + }); + if let Some(ExportedSymbol::NonGeneric(id)) = sym { + let start_def_id = id.expect_local(); + let start_span = tcx.def_span(start_def_id); + + let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig( + [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))], + tcx.types.isize, + false, + hir::Safety::Safe, + Abi::Rust, + )); + + let correct_func_sig = check_function_signature( + tcx, + ObligationCause::new(start_span, start_def_id, ObligationCauseCode::Misc), + *id, + expected_sig, + ) + .is_ok(); + + if correct_func_sig { + (*id, EntryFnType::Start) + } else { + tcx.dcx().fatal( + "`miri_start` must have the following signature:\n\ + fn miri_start(argc: isize, argv: *const *const u8) -> isize", + ); + } + } else { + tcx.dcx().fatal( + "Miri can only run programs that have a main function.\n\ + Alternatively, you can export a `miri_start` function:\n\ + \n\ + #[cfg(miri)]\n\ + #[no_mangle]\n\ + fn miri_start(argc: isize, argv: *const *const u8) -> isize {\ + \n // Call the actual start function that your project implements, based on your target's conventions.\n\ + }" + ); + } +} + fn main() { #[cfg(any(target_os = "linux", target_os = "macos"))] jemalloc_magic(); @@ -421,7 +475,9 @@ fn main() { } else if arg == "--" { after_dashdash = true; } else if arg == "-Zmiri-disable-validation" { - miri_config.validate = false; + miri_config.validation = ValidationMode::No; + } else if arg == "-Zmiri-recursive-validation" { + miri_config.validation = ValidationMode::Deep; } else if arg == "-Zmiri-disable-stacked-borrows" { miri_config.borrow_tracker = None; } else if arg == "-Zmiri-tree-borrows" { 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 1d75486a78189..fc2b3f9c6ea77 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -673,7 +673,8 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // attempt to use it for a non-zero-sized access. // Dangling slices are a common case here; it's valid to get their length but with raw // pointer tagging for example all calls to get_unchecked on them are invalid. - if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr()) { + if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr(), 0) + { log_creation(this, Some((alloc_id, base_offset, orig_tag)))?; // Still give it the new provenance, it got retagged after all. return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag })); @@ -685,7 +686,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { } } - let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr())?; + let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr(), 0)?; log_creation(this, Some((alloc_id, base_offset, orig_tag)))?; trace!( 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 123d4b407fb4c..44f42d5fb9cd9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -223,7 +223,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; trace!("Reborrow of size {:?}", ptr_size); - let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr()) { + let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) { Ok(data) => { // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation. // After all, the pointer may be lazily initialized outside this initial range. diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 2baa09bec168b..9df0d95f1f275 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -1180,7 +1180,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // We avoid `get_ptr_alloc` since we do *not* want to run the access hooks -- the actual // access will happen later. let (alloc_id, _offset, _prov) = this - .ptr_try_get_alloc_id(place.ptr()) + .ptr_try_get_alloc_id(place.ptr(), 0) .expect("there are no zero-sized atomic accesses"); if this.get_alloc_mutability(alloc_id)? == Mutability::Not { // See if this is fine. @@ -1307,7 +1307,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { if let Some(data_race) = &this.machine.data_race { if data_race.race_detecting() { let size = place.layout.size; - let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr())?; + let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0)?; // Load and log the atomic operation. // Note that atomic loads are possible even from read-only allocations, so `get_alloc_extra_mut` is not an option. let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index a53dd7eac1e9b..f72591f0c4bdf 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -256,7 +256,7 @@ pub struct Thread<'tcx> { /// which then forwards it to 'Resume'. However this argument is implicit in MIR, /// so we have to store it out-of-band. When there are multiple active unwinds, /// the innermost one is always caught first, so we can store them as a stack. - pub(crate) panic_payloads: Vec, + pub(crate) panic_payloads: Vec>, /// Last OS error location in memory. It is a 32-bit integer. pub(crate) last_error: Option>, @@ -377,10 +377,6 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { return_place, locals, extra, - body: _, - instance: _, - return_to_block: _, - loc: _, // There are some private fields we cannot access; they contain no tags. .. } = self; @@ -952,7 +948,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( instance, start_abi, - &[*func_arg], + &[func_arg], Some(&ret_place), StackPopCleanup::Root { cleanup: true }, )?; diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index e92eaa8f91f75..6f4171584a8f7 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -468,7 +468,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { init: Scalar, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?; + let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?; if let ( crate::AllocExtra { weak_memory: Some(alloc_buffers), .. }, crate::MiriMachine { data_race: Some(global), threads, .. }, @@ -495,7 +495,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); if let Some(global) = &this.machine.data_race { - let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?; + let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?; if let Some(alloc_buffers) = this.get_alloc_extra(alloc_id)?.weak_memory.as_ref() { if atomic == AtomicReadOrd::SeqCst { global.sc_read(&this.machine.threads); @@ -535,7 +535,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { init: Scalar, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr())?; + let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr(), 0)?; if let ( crate::AllocExtra { weak_memory: Some(alloc_buffers), .. }, crate::MiriMachine { data_race: Some(global), threads, .. }, @@ -585,7 +585,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { global.sc_read(&this.machine.threads); } let size = place.layout.size; - let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?; + let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?; if let Some(alloc_buffers) = this.get_alloc_extra(alloc_id)?.weak_memory.as_ref() { let buffer = alloc_buffers .get_or_create_store_buffer(alloc_range(base_offset, size), init)?; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 2184a4426c8dc..1173da4697564 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -68,7 +68,7 @@ pub enum IsolatedOp { Allow, } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum BacktraceStyle { /// Prints a terser backtrace which ideally only contains relevant information. Short, @@ -78,6 +78,16 @@ pub enum BacktraceStyle { Off, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ValidationMode { + /// Do not perform any kind of validation. + No, + /// Validate the interior of the value, but not things behind references. + Shallow, + /// Fully recursively validate references. + Deep, +} + /// Configuration needed to spawn a Miri instance. #[derive(Clone)] pub struct MiriConfig { @@ -85,7 +95,7 @@ pub struct MiriConfig { /// (This is still subject to isolation as well as `forwarded_env_vars`.) pub env: Vec<(OsString, OsString)>, /// Determine if validity checking is enabled. - pub validate: bool, + pub validation: ValidationMode, /// Determines if Stacked Borrows or Tree Borrows is enabled. pub borrow_tracker: Option, /// Whether `core::ptr::Unique` receives special treatment. @@ -162,7 +172,7 @@ impl Default for MiriConfig { fn default() -> MiriConfig { MiriConfig { env: vec![], - validate: true, + validation: ValidationMode::Shallow, borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows), unique_is_unique: false, check_alignment: AlignmentCheck::Int, @@ -297,7 +307,8 @@ pub fn create_ecx<'tcx>( // First argument is constructed later, because it's skipped if the entry function uses #[start]. // Second argument (argc): length of `config.args`. - let argc = Scalar::from_target_usize(u64::try_from(config.args.len()).unwrap(), &ecx); + let argc = + ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize); // Third argument (`argv`): created from `config.args`. let argv = { // Put each argument in memory, collect pointers. @@ -324,13 +335,11 @@ pub fn create_ecx<'tcx>( ecx.write_immediate(arg, &place)?; } ecx.mark_immutable(&argvs_place); - // A pointer to that place is the 3rd argument for main. - let argv = argvs_place.to_ref(&ecx); // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`. { let argc_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?; - ecx.write_scalar(argc, &argc_place)?; + ecx.write_immediate(*argc, &argc_place)?; ecx.mark_immutable(&argc_place); ecx.machine.argc = Some(argc_place.ptr()); @@ -338,7 +347,7 @@ pub fn create_ecx<'tcx>( ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, MiriMemoryKind::Machine.into(), )?; - ecx.write_immediate(argv, &argv_place)?; + ecx.write_pointer(argvs_place.ptr(), &argv_place)?; ecx.mark_immutable(&argv_place); ecx.machine.argv = Some(argv_place.ptr()); } @@ -359,7 +368,7 @@ pub fn create_ecx<'tcx>( } ecx.mark_immutable(&cmd_place); } - argv + ecx.mplace_to_ref(&argvs_place)? }; // Return place (in static memory so that it does not count as leak). @@ -395,10 +404,14 @@ pub fn create_ecx<'tcx>( start_instance, Abi::Rust, &[ - Scalar::from_pointer(main_ptr, &ecx).into(), - argc.into(), + ImmTy::from_scalar( + Scalar::from_pointer(main_ptr, &ecx), + // FIXME use a proper fn ptr type + ecx.machine.layouts.const_raw_ptr, + ), + argc, argv, - Scalar::from_u8(sigpipe).into(), + ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8), ], Some(&ret_place), StackPopCleanup::Root { cleanup: true }, @@ -408,7 +421,7 @@ pub fn create_ecx<'tcx>( ecx.call_function( entry_instance, Abi::Rust, - &[argc.into(), argv], + &[argc, argv], Some(&ret_place), StackPopCleanup::Root { cleanup: true }, )?; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index ba094c988e5ab..1bdf9f06dcdb0 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -12,13 +12,14 @@ use rustc_apfloat::Float; use rustc_hir::{ def::{DefKind, Namespace}, def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}, + Safety, }; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir; -use rustc_middle::ty::layout::MaybeResult; +use rustc_middle::ty::layout::{FnAbiOf, MaybeResult}; use rustc_middle::ty::{ self, layout::{LayoutOf, TyAndLayout}, @@ -492,48 +493,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, f: ty::Instance<'tcx>, caller_abi: Abi, - args: &[Immediate], + args: &[ImmTy<'tcx>], dest: Option<&MPlaceTy<'tcx>>, stack_pop: StackPopCleanup, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private. - let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi(); - if callee_abi != caller_abi { - throw_ub_format!( - "calling a function with ABI {} using caller ABI {}", - callee_abi.name(), - caller_abi.name() - ) - } - // Push frame. + // Get MIR. let mir = this.load_mir(f.def, None)?; let dest = match dest { Some(dest) => dest.clone(), None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?), }; - this.push_stack_frame(f, mir, &dest, stack_pop)?; - - // Initialize arguments. - let mut callee_args = this.frame().body.args_iter(); - for arg in args { - let local = callee_args - .next() - .ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?; - // Make the local live, and insert the initial value. - this.storage_live(local)?; - let callee_arg = this.local_to_place(local)?; - this.write_immediate(*arg, &callee_arg)?; - } - if callee_args.next().is_some() { - throw_ub_format!("callee has more arguments than expected"); - } - - // Initialize remaining locals. - this.storage_live_for_always_live_locals()?; - Ok(()) + // Construct a function pointer type representing the caller perspective. + let sig = this.tcx.mk_fn_sig( + args.iter().map(|a| a.layout.ty), + dest.layout.ty, + /*c_variadic*/ false, + Safety::Safe, + caller_abi, + ); + let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + + this.init_stack_frame( + f, + mir, + caller_fn_abi, + &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::>(), + /*with_caller_location*/ false, + &dest, + stack_pop, + ) } /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter @@ -606,7 +597,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // The part between the end_ptr and the end of the place is also frozen. // So pretend there is a 0-sized `UnsafeCell` at the end. - unsafe_cell_action(&place.ptr().offset(size, this)?, Size::ZERO)?; + unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?; // Done! return Ok(()); @@ -975,7 +966,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { loop { // FIXME: We are re-getting the allocation each time around the loop. // Would be nice if we could somehow "extend" an existing AllocRange. - let alloc = this.get_ptr_alloc(ptr.offset(len, this)?, size1)?.unwrap(); // not a ZST, so we will get a result + let alloc = this.get_ptr_alloc(ptr.wrapping_offset(len, this), size1)?.unwrap(); // not a ZST, so we will get a result let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?; if byte == 0 { break; @@ -1039,7 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { break; } else { wchars.push(wchar_int.try_into().unwrap()); - ptr = ptr.offset(size, this)?; + ptr = ptr.wrapping_offset(size, this); } } @@ -1114,12 +1105,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Make an attempt to get at the instance of the function this is inlined from. let instance: Option<_> = try { let scope = frame.current_source_info()?.scope; - let inlined_parent = frame.body.source_scopes[scope].inlined_parent_scope?; - let source = &frame.body.source_scopes[inlined_parent]; + let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?; + let source = &frame.body().source_scopes[inlined_parent]; source.inlined.expect("inlined_parent_scope points to scope without inline info").0 }; // Fall back to the instance of the function itself. - let instance = instance.unwrap_or(frame.instance); + let instance = instance.unwrap_or(frame.instance()); // Now check the crate it is in. We could try to be clever here and e.g. check if this is // the same crate as `start_fn`, but that would not work for running std tests in Miri, so // we'd need some more hacks anyway. So we just check the name of the crate. If someone @@ -1359,9 +1350,9 @@ impl<'tcx> MiriMachine<'tcx> { /// This is the source of truth for the `is_user_relevant` flag in our `FrameExtra`. pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool { - let def_id = frame.instance.def_id(); + let def_id = frame.instance().def_id(); (def_id.is_local() || self.local_crates.contains(&def_id.krate)) - && !frame.instance.def.requires_caller_location(self.tcx) + && !frame.instance().def.requires_caller_location(self.tcx) } } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 9cd776c937101..18b22827bdb15 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -33,7 +33,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); // See if the core engine can handle this intrinsic. - if this.emulate_intrinsic(instance, args, dest, ret)? { + if this.eval_intrinsic(instance, args, dest, ret)? { return Ok(None); } let intrinsic_name = this.tcx.item_name(instance.def_id()); @@ -153,7 +153,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Would not be considered UB, or the other way around (`is_val_statically_known(0)`). "is_val_statically_known" => { let [arg] = check_arg_count(args)?; - this.validate_operand(arg)?; + this.validate_operand(arg, /*recursive*/ false)?; let branch: bool = this.machine.rng.get_mut().gen(); this.write_scalar(Scalar::from_bool(branch), dest)?; } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 7fb68d782f11d..2b3ae6df5de8a 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -10,7 +10,6 @@ #![feature(yeet_expr)] #![feature(nonzero_ops)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![feature(trait_upcasting)] #![feature(strict_overflow_ops)] #![feature(is_none_or)] @@ -143,6 +142,7 @@ pub use crate::diagnostics::{ }; pub use crate::eval::{ create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith, + ValidationMode, }; pub use crate::helpers::{AccessKind, EvalContextExt as _}; pub use crate::machine::{ diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index e492793a65135..94598e7d2e3cc 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -187,7 +187,11 @@ impl fmt::Display for MiriMemoryKind { pub type MemoryKind = interpret::MemoryKind; /// Pointer provenance. -#[derive(Clone, Copy)] +// This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking +// *might* be recursive and then it has to track which places have already been visited. +// These implementations are a bit questionable, and it means we may check the same place multiple +// times with different provenance, but that is in general not wrong. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum Provenance { /// For pointers with concrete provenance. we exactly know which allocation they are attached to /// and what their borrow tag is. @@ -215,24 +219,6 @@ pub enum Provenance { Wildcard, } -// This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking -// *might* be recursive and then it has to track which places have already been visited. -// However, comparing provenance is meaningless, since `Wildcard` might be any provenance -- and of -// course we don't actually do recursive checking. -// We could change `RefTracking` to strip provenance for its `seen` set but that type is generic so that is quite annoying. -// Instead owe add the required instances but make them panic. -impl PartialEq for Provenance { - fn eq(&self, _other: &Self) -> bool { - panic!("Provenance must not be compared") - } -} -impl Eq for Provenance {} -impl std::hash::Hash for Provenance { - fn hash(&self, _state: &mut H) { - panic!("Provenance must not be hashed") - } -} - /// The "extra" information a pointer has over a regular AllocId. #[derive(Copy, Clone, PartialEq)] pub enum ProvenanceExtra { @@ -460,7 +446,7 @@ pub struct MiriMachine<'tcx> { pub(crate) isolated_op: IsolatedOp, /// Whether to enforce the validity invariant. - pub(crate) validate: bool, + pub(crate) validation: ValidationMode, /// The table of file descriptors. pub(crate) fds: shims::FdTable, @@ -659,7 +645,7 @@ impl<'tcx> MiriMachine<'tcx> { cmd_line: None, tls: TlsData::default(), isolated_op: config.isolated_op, - validate: config.validate, + validation: config.validation, fds: shims::FdTable::init(config.mute_stdout_stderr), dirs: Default::default(), layouts, @@ -801,7 +787,7 @@ impl VisitProvenance for MiriMachine<'_> { fds, tcx: _, isolated_op: _, - validate: _, + validation: _, clock: _, layouts: _, static_roots: _, @@ -943,7 +929,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { #[inline(always)] fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool { - ecx.machine.validate + ecx.machine.validation != ValidationMode::No + } + #[inline(always)] + fn enforce_validity_recursively( + ecx: &InterpCx<'tcx, Self>, + _layout: TyAndLayout<'tcx>, + ) -> bool { + ecx.machine.validation == ValidationMode::Deep } #[inline(always)] @@ -1198,19 +1191,23 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } } - /// Convert a pointer with provenance into an allocation-offset pair, - /// or a `None` with an absolute address if that conversion is not possible. + /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info. + /// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can + /// be used to disambiguate situations where a wildcard pointer sits right in between two + /// allocations. /// - /// This is called when a pointer is about to be used for memory access, - /// an in-bounds check, or anything else that requires knowing which allocation it points to. + /// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`. /// The resulting `AllocId` will just be used for that one step and the forgotten again /// (i.e., we'll never turn the data returned here back into a `Pointer` that might be /// stored in machine state). + /// + /// When this fails, that means the pointer does not point to a live allocation. fn ptr_get_alloc( ecx: &MiriInterpCx<'tcx>, ptr: StrictPointer, + size: i64, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { - let rel = ecx.ptr_get_alloc(ptr); + let rel = ecx.ptr_get_alloc(ptr, size); rel.map(|(alloc_id, size)| { let tag = match ptr.provenance { @@ -1355,7 +1352,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> { // Start recording our event before doing anything else let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() { - let fn_name = frame.instance.to_string(); + let fn_name = frame.instance().to_string(); let entry = ecx.machine.string_cache.entry(fn_name.clone()); let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name)); @@ -1446,7 +1443,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our // concurrency and what it prints is just plain wrong. So we print our own information // instead. (Cc https://github.com/rust-lang/miri/issues/2266) - info!("Leaving {}", ecx.frame().instance); + info!("Leaving {}", ecx.frame().instance()); Ok(()) } @@ -1476,7 +1473,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Needs to be done after dropping frame to show up on the right nesting level. // (Cc https://github.com/rust-lang/miri/issues/2266) if !ecx.active_thread_stack().is_empty() { - info!("Continuing in {}", ecx.frame().instance); + info!("Continuing in {}", ecx.frame().instance()); } res } @@ -1489,7 +1486,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else { panic!("after_local_allocated should only be called on fresh allocations"); }; - let local_decl = &ecx.frame().body.local_decls[local]; + let local_decl = &ecx.frame().body().local_decls[local]; let span = local_decl.source_info.span; ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None)); Ok(()) diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 24a4b5f26a950..9bb6777a9b047 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -46,8 +46,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut data = Vec::new(); for frame in this.active_thread_stack().iter().rev() { // Match behavior of debuginfo (`FunctionCx::adjusted_span_and_dbg_scope`). - let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body.span); - data.push((frame.instance, span.lo())); + let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body().span); + data.push((frame.instance(), span.lo())); } let ptrs: Vec<_> = data @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr = this.read_pointer(ptr)?; // Take apart the pointer, we need its pieces. The offset encodes the span. - let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr, 0)?; // This has to be an actual global fn ptr, not a dlsym function. let fn_instance = if let Some(GlobalAlloc::Function { instance, .. }) = diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 9004f7efc8b5e..7f6f63ff5e79b 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -278,7 +278,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_get_alloc_id" => { let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; - let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr).map_err(|_e| { + let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}" ))) @@ -311,7 +311,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_static_root" => { let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; - let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?; if offset != Size::ZERO { throw_unsup_format!( "pointer passed to `miri_static_root` must point to beginning of an allocated block" @@ -392,7 +392,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "`miri_promise_symbolic_alignment`: pointer is not actually aligned" ); } - if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr) { + if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) { let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id); // If the newly promised alignment is bigger than the native alignment of this // allocation, and bigger than the previously promised alignment, then set it. @@ -584,8 +584,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let n = Size::from_bytes(this.read_target_usize(n)?); // C requires that this must always be a valid pointer (C18 §7.1.4). - this.ptr_get_alloc_id(left)?; - this.ptr_get_alloc_id(right)?; + this.ptr_get_alloc_id(left, 0)?; + this.ptr_get_alloc_id(right, 0)?; let result = { let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?; @@ -612,7 +612,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = val as u8; // C requires that this must always be a valid pointer (C18 §7.1.4). - this.ptr_get_alloc_id(ptr)?; + this.ptr_get_alloc_id(ptr, 0)?; if let Some(idx) = this .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))? @@ -622,7 +622,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { { let idx = u64::try_from(idx).unwrap(); #[allow(clippy::arithmetic_side_effects)] // idx < num, so this never wraps - let new_ptr = ptr.offset(Size::from_bytes(num - idx - 1), this)?; + let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this); this.write_pointer(new_ptr, dest)?; } else { this.write_null(dest)?; @@ -639,14 +639,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = val as u8; // C requires that this must always be a valid pointer (C18 §7.1.4). - this.ptr_get_alloc_id(ptr)?; + this.ptr_get_alloc_id(ptr, 0)?; let idx = this .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))? .iter() .position(|&c| c == val); if let Some(idx) = idx { - let new_ptr = ptr.offset(Size::from_bytes(idx as u64), this)?; + let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx as u64), this); this.write_pointer(new_ptr, dest)?; } else { this.write_null(dest)?; @@ -681,8 +681,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that. // (This is more than Rust requires, so `mem_copy` is not sufficient.) - this.ptr_get_alloc_id(ptr_dest)?; - this.ptr_get_alloc_id(ptr_src)?; + this.ptr_get_alloc_id(ptr_dest, 0)?; + this.ptr_get_alloc_id(ptr_src, 0)?; this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?; this.write_pointer(ptr_dest, dest)?; diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 306dce5edcd3d..ab705ddccab6e 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -25,7 +25,7 @@ pub struct CatchUnwindData<'tcx> { /// The `catch_fn` callback to call in case of a panic. catch_fn: Pointer, /// The `data` argument for that callback. - data: Scalar, + data: ImmTy<'tcx>, /// The return place from the original call to `try`. dest: MPlaceTy<'tcx>, /// The return block from the original call to `try`. @@ -48,9 +48,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - trace!("miri_start_unwind: {:?}", this.frame().instance); + trace!("miri_start_unwind: {:?}", this.frame().instance()); - let payload = this.read_scalar(payload)?; + let payload = this.read_immediate(payload)?; let thread = this.active_thread_mut(); thread.panic_payloads.push(payload); @@ -80,7 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Get all the arguments. let [try_fn, data, catch_fn] = check_arg_count(args)?; let try_fn = this.read_pointer(try_fn)?; - let data = this.read_scalar(data)?; + let data = this.read_immediate(data)?; let catch_fn = this.read_pointer(catch_fn)?; // Now we make a function call, and pass `data` as first and only argument. @@ -89,7 +89,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( f_instance, Abi::Rust, - &[data.into()], + &[data.clone()], None, // Directly return to caller. StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue }, @@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and we are unwinding, so we should catch that. trace!( "unwinding: found catch_panic frame during unwinding: {:?}", - this.frame().instance + this.frame().instance() ); // We set the return value of `try` to 1, since there was a panic. @@ -140,7 +140,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( f_instance, Abi::Rust, - &[catch_unwind.data.into(), payload.into()], + &[catch_unwind.data, payload], None, // Directly return to caller of `try`. StackPopCleanup::Goto { @@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic, Abi::Rust, - &[msg.to_ref(this)], + &[this.mplace_to_ref(&msg)?], None, StackPopCleanup::Goto { ret: None, unwind }, ) @@ -188,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic, Abi::Rust, - &[msg.to_ref(this)], + &[this.mplace_to_ref(&msg)?], None, StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable }, ) @@ -207,9 +207,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Forward to `panic_bounds_check` lang item. // First arg: index. - let index = this.read_scalar(&this.eval_operand(index, None)?)?; + let index = this.read_immediate(&this.eval_operand(index, None)?)?; // Second arg: len. - let len = this.read_scalar(&this.eval_operand(len, None)?)?; + let len = this.read_immediate(&this.eval_operand(len, None)?)?; // Call the lang item. let panic_bounds_check = this.tcx.lang_items().panic_bounds_check_fn().unwrap(); @@ -217,7 +217,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic_bounds_check, Abi::Rust, - &[index.into(), len.into()], + &[index, len], None, StackPopCleanup::Goto { ret: None, unwind }, )?; @@ -226,9 +226,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Forward to `panic_misaligned_pointer_dereference` lang item. // First arg: required. - let required = this.read_scalar(&this.eval_operand(required, None)?)?; + let required = this.read_immediate(&this.eval_operand(required, None)?)?; // Second arg: found. - let found = this.read_scalar(&this.eval_operand(found, None)?)?; + let found = this.read_immediate(&this.eval_operand(found, None)?)?; // Call the lang item. let panic_misaligned_pointer_dereference = @@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( panic_misaligned_pointer_dereference, Abi::Rust, - &[required.into(), found.into()], + &[required, found], None, StackPopCleanup::Goto { ret: None, unwind }, )?; diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index e8f906d37e842..ebec1a70c5cd0 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -93,7 +93,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Scalar::from_i32(0)) } - fn gettimeofday(&mut self, tv_op: &OpTy<'tcx>, tz_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn gettimeofday( + &mut self, + tv_op: &OpTy<'tcx>, + tz_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("gettimeofday"); @@ -106,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !this.ptr_is_null(tz)? { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let duration = system_time_to_duration(&SystemTime::now())?; @@ -115,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?; - Ok(0) + Ok(Scalar::from_i32(0)) } // The localtime() function shall convert the time in seconds since the Epoch pointed to by @@ -308,7 +312,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, req_op: &OpTy<'tcx>, _rem: &OpTy<'tcx>, // Signal handlers are not supported, so rem will never be written to. - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("nanosleep"); @@ -320,7 +324,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { None => { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } }; @@ -333,7 +337,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { @timeout = |_this| { Ok(()) } ), ); - Ok(0) + Ok(Scalar::from_i32(0)) } #[allow(non_snake_case)] diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 8707446878955..52d83cd72997e 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -315,6 +315,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: Technically, the reason should be `DLL_PROCESS_DETACH` when the main thread exits // but std treats both the same. let reason = this.eval_windows("c", "DLL_THREAD_DETACH"); + let null_ptr = + ImmTy::from_scalar(Scalar::null_ptr(this), this.machine.layouts.const_raw_ptr); // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown @@ -322,7 +324,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( thread_callback, Abi::System { unwind: false }, - &[Scalar::null_ptr(this).into(), reason.into(), Scalar::null_ptr(this).into()], + &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr], None, StackPopCleanup::Root { cleanup: true }, )?; @@ -343,7 +345,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( instance, Abi::C { unwind: false }, - &[data.into()], + &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)], None, StackPopCleanup::Root { cleanup: true }, )?; @@ -380,7 +382,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.call_function( instance, Abi::C { unwind: false }, - &[ptr.into()], + &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)], None, StackPopCleanup::Root { cleanup: true }, )?; diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 3b8ad65195b8a..54bf3a3ae8295 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -81,8 +81,10 @@ impl<'tcx> UnixEnvVars<'tcx> { return Ok(None); }; // The offset is used to strip the "{name}=" part of the string. - let var_ptr = var_ptr - .offset(Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), ecx)?; + let var_ptr = var_ptr.wrapping_offset( + Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), + ecx, + ); Ok(Some(var_ptr)) } @@ -148,7 +150,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(var_ptr.unwrap_or_else(Pointer::null)) } - fn setenv(&mut self, name_op: &OpTy<'tcx>, value_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn setenv( + &mut self, + name_op: &OpTy<'tcx>, + value_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("setenv"); @@ -169,16 +175,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(0) // return zero on success + Ok(Scalar::from_i32(0)) // return zero on success } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(-1) + Ok(Scalar::from_i32(-1)) } } - fn unsetenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn unsetenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("unsetenv"); @@ -195,12 +201,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } this.update_environ()?; - Ok(0) + Ok(Scalar::from_i32(0)) } else { // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - Ok(-1) + Ok(Scalar::from_i32(-1)) } } @@ -232,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(Pointer::null()) } - fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("chdir"); @@ -242,16 +248,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`chdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } - match env::set_current_dir(path) { - Ok(()) => Ok(0), - Err(e) => { - this.set_last_error_from_io_error(e)?; - Ok(-1) - } - } + let result = env::set_current_dir(path).map(|()| 0); + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } /// Updates the `environ` static. @@ -270,18 +271,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(()) } - fn getpid(&mut self) -> InterpResult<'tcx, i32> { + fn getpid(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("getpid"); // The reason we need to do this wacky of a conversion is because // `libc::getpid` returns an i32, however, `std::process::id()` return an u32. // So we un-do the conversion that stdlib does and turn it back into an i32. - #[allow(clippy::cast_possible_wrap)] - Ok(this.get_pid() as i32) + // In `Scalar` representation, these are the same, so we don't need to anything else. + Ok(Scalar::from_u32(this.get_pid())) } - fn linux_gettid(&mut self) -> InterpResult<'tcx, i32> { + fn linux_gettid(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); this.assert_target_os("linux", "gettid"); @@ -290,7 +291,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Compute a TID for this thread, ensuring that the main thread has PID == TID. let tid = this.get_pid().strict_add(index); - #[allow(clippy::cast_possible_wrap)] - Ok(tid as i32) + Ok(Scalar::from_u32(tid)) } } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 0fffecd99d5cb..1b25ef05769b9 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -12,6 +12,13 @@ use rustc_target::abi::Size; use crate::shims::unix::*; use crate::*; +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub(crate) enum FlockOp { + SharedLock { nonblocking: bool }, + ExclusiveLock { nonblocking: bool }, + Unlock, +} + /// Represents an open file descriptor. pub trait FileDescription: std::fmt::Debug + Any { fn name(&self) -> &'static str; @@ -77,6 +84,14 @@ pub trait FileDescription: std::fmt::Debug + Any { throw_unsup_format!("cannot close {}", self.name()); } + fn flock<'tcx>( + &self, + _communicate_allowed: bool, + _op: FlockOp, + ) -> InterpResult<'tcx, io::Result<()>> { + throw_unsup_format!("cannot flock {}", self.name()); + } + fn is_tty(&self, _communicate_allowed: bool) -> bool { // Most FDs are not tty's and the consequence of a wrong `false` are minor, // so we use a default impl here. @@ -189,9 +204,13 @@ impl FileDescription for NullOutput { } #[derive(Clone, Debug)] -pub struct FileDescriptor(Rc>>); +pub struct FileDescriptionRef(Rc>>); + +impl FileDescriptionRef { + fn new(fd: impl FileDescription) -> Self { + FileDescriptionRef(Rc::new(RefCell::new(Box::new(fd)))) + } -impl FileDescriptor { pub fn borrow(&self) -> Ref<'_, dyn FileDescription> { Ref::map(self.0.borrow(), |fd| fd.as_ref()) } @@ -213,7 +232,7 @@ impl FileDescriptor { /// The file descriptor table #[derive(Debug)] pub struct FdTable { - pub fds: BTreeMap, + fds: BTreeMap, } impl VisitProvenance for FdTable { @@ -228,25 +247,25 @@ impl FdTable { } pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable { let mut fds = FdTable::new(); - fds.insert_fd(io::stdin()); + fds.insert_new(io::stdin()); if mute_stdout_stderr { - assert_eq!(fds.insert_fd(NullOutput), 1); - assert_eq!(fds.insert_fd(NullOutput), 2); + assert_eq!(fds.insert_new(NullOutput), 1); + assert_eq!(fds.insert_new(NullOutput), 2); } else { - assert_eq!(fds.insert_fd(io::stdout()), 1); - assert_eq!(fds.insert_fd(io::stderr()), 2); + assert_eq!(fds.insert_new(io::stdout()), 1); + assert_eq!(fds.insert_new(io::stderr()), 2); } fds } - /// Insert a file descriptor to the FdTable. - pub fn insert_fd(&mut self, fd: T) -> i32 { - let file_handle = FileDescriptor(Rc::new(RefCell::new(Box::new(fd)))); - self.insert_fd_with_min_fd(file_handle, 0) + /// Insert a new file description to the FdTable. + pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 { + let file_handle = FileDescriptionRef::new(fd); + self.insert_ref_with_min_fd(file_handle, 0) } - /// Insert a new FD that is at least `min_fd`. - pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32) -> i32 { + /// Insert a file description, giving it a file descriptor that is at least `min_fd`. + fn insert_ref_with_min_fd(&mut self, file_handle: FileDescriptionRef, min_fd: i32) -> i32 { // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in // between used FDs, the find_map combinator will return it. If the first such unused FD // is after all other used FDs, the find_map combinator will return None, and we will use @@ -282,12 +301,12 @@ impl FdTable { Some(fd.borrow_mut()) } - pub fn dup(&self, fd: i32) -> Option { + pub fn get_ref(&self, fd: i32) -> Option { let fd = self.fds.get(&fd)?; Some(fd.clone()) } - pub fn remove(&mut self, fd: i32) -> Option { + pub fn remove(&mut self, fd: i32) -> Option { self.fds.remove(&fd) } @@ -298,33 +317,67 @@ impl FdTable { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, i32> { + fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(dup_fd) = this.machine.fds.dup(old_fd) else { - return this.fd_not_found(); + let Some(dup_fd) = this.machine.fds.get_ref(old_fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); }; - Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, 0)) + Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, 0))) } - fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, i32> { + fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(dup_fd) = this.machine.fds.dup(old_fd) else { - return this.fd_not_found(); + let Some(dup_fd) = this.machine.fds.get_ref(old_fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); }; if new_fd != old_fd { // Close new_fd if it is previously opened. // If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive. - if let Some(file_descriptor) = this.machine.fds.fds.insert(new_fd, dup_fd) { + if let Some(file_description) = this.machine.fds.fds.insert(new_fd, dup_fd) { // Ignore close error (not interpreter's) according to dup2() doc. - file_descriptor.close(this.machine.communicate())?.ok(); + file_description.close(this.machine.communicate())?.ok(); } } - Ok(new_fd) + Ok(Scalar::from_i32(new_fd)) + } + + fn flock(&mut self, fd: i32, op: i32) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let Some(file_descriptor) = this.machine.fds.get(fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); + }; + + // We need to check that there aren't unsupported options in `op`. + let lock_sh = this.eval_libc_i32("LOCK_SH"); + let lock_ex = this.eval_libc_i32("LOCK_EX"); + let lock_nb = this.eval_libc_i32("LOCK_NB"); + let lock_un = this.eval_libc_i32("LOCK_UN"); + + use FlockOp::*; + let parsed_op = if op == lock_sh { + SharedLock { nonblocking: false } + } else if op == lock_sh | lock_nb { + SharedLock { nonblocking: true } + } else if op == lock_ex { + ExclusiveLock { nonblocking: false } + } else if op == lock_ex | lock_nb { + ExclusiveLock { nonblocking: true } + } else if op == lock_un { + Unlock + } else { + throw_unsup_format!("unsupported flags {:#x}", op); + }; + + let result = file_descriptor.flock(this.machine.communicate(), parsed_op)?; + drop(file_descriptor); + // return `0` if flock is successful + let result = result.map(|()| 0i32); + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } - fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> { + fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); if args.len() < 2 { @@ -342,11 +395,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` // always sets this flag when opening a file. However we still need to check that the // file itself is open. - if this.machine.fds.is_fd(fd) { - Ok(this.eval_libc_i32("FD_CLOEXEC")) + Ok(Scalar::from_i32(if this.machine.fds.is_fd(fd) { + this.eval_libc_i32("FD_CLOEXEC") } else { - this.fd_not_found() - } + this.fd_not_found()? + })) } else if cmd == this.eval_libc_i32("F_DUPFD") || cmd == this.eval_libc_i32("F_DUPFD_CLOEXEC") { @@ -362,16 +415,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let start = this.read_scalar(&args[2])?.to_i32()?; - match this.machine.fds.dup(fd) { - Some(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)), - None => this.fd_not_found(), + match this.machine.fds.get_ref(fd) { + Some(dup_fd) => + Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, start))), + None => Ok(Scalar::from_i32(this.fd_not_found()?)), } } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } this.ffullsync_fd(fd) @@ -385,10 +439,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let fd = this.read_scalar(fd_op)?.to_i32()?; - let Some(file_descriptor) = this.machine.fds.remove(fd) else { + let Some(file_description) = this.machine.fds.remove(fd) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; - let result = file_descriptor.close(this.machine.communicate())?; + let result = file_description.close(this.machine.communicate())?; // return `0` if close is successful let result = result.map(|()| 0i32); Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) @@ -416,7 +470,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buf: Pointer, count: u64, offset: Option, - ) -> InterpResult<'tcx, i64> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -434,9 +488,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let communicate = this.machine.communicate(); // We temporarily dup the FD to be able to retain mutable access to `this`. - let Some(fd) = this.machine.fds.dup(fd) else { + let Some(fd) = this.machine.fds.get_ref(fd) else { trace!("read: FD not found"); - return this.fd_not_found(); + return Ok(Scalar::from_target_isize(this.fd_not_found()?, this)); }; trace!("read: FD mapped to {fd:?}"); @@ -450,7 +504,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Ok(offset) = u64::try_from(offset) else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(-1); + return Ok(Scalar::from_target_isize(-1, this)); }; fd.borrow_mut().pread(communicate, &mut bytes, offset, this) } @@ -467,11 +521,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buf, bytes[..usize::try_from(read_bytes).unwrap()].iter().copied(), )?; - Ok(read_bytes) + Ok(Scalar::from_target_isize(read_bytes, this)) } Err(e) => { this.set_last_error_from_io_error(e)?; - Ok(-1) + Ok(Scalar::from_target_isize(-1, this)) } } } @@ -482,7 +536,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { buf: Pointer, count: u64, offset: Option, - ) -> InterpResult<'tcx, i64> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -499,8 +553,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned(); // We temporarily dup the FD to be able to retain mutable access to `this`. - let Some(fd) = this.machine.fds.dup(fd) else { - return this.fd_not_found(); + let Some(fd) = this.machine.fds.get_ref(fd) else { + return Ok(Scalar::from_target_isize(this.fd_not_found()?, this)); }; let result = match offset { @@ -509,7 +563,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Ok(offset) = u64::try_from(offset) else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(-1); + return Ok(Scalar::from_target_isize(-1, this)); }; fd.borrow_mut().pwrite(communicate, &bytes, offset, this) } @@ -517,6 +571,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { drop(fd); let result = result?.map(|c| i64::try_from(c).unwrap()); - this.try_unwrap_io_result(result) + Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this)) } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 966e590fcc41e..57930f9807d62 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -62,13 +62,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "unsetenv" => { let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.unsetenv(name)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "setenv" => { let [name, value, overwrite] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.read_scalar(overwrite)?.to_i32()?; let result = this.setenv(name, value)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "getcwd" => { let [buf, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -78,12 +78,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "chdir" => { let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.chdir(path)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "getpid" => { let [] = this.check_shim(abi, Abi::C { unwind: false}, link_name, args)?; let result = this.getpid()?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } // File descriptors @@ -93,7 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; let result = this.read(fd, buf, count, None)?; - this.write_scalar(Scalar::from_target_isize(result, this), dest)?; + this.write_scalar(result, dest)?; } "write" => { let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -103,7 +103,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); let result = this.write(fd, buf, count, None)?; // Now, `result` is the value we return back to the program. - this.write_scalar(Scalar::from_target_isize(result, this), dest)?; + this.write_scalar(result, dest)?; } "pread" => { let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -112,7 +112,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let count = this.read_target_usize(count)?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let result = this.read(fd, buf, count, Some(offset))?; - this.write_scalar(Scalar::from_target_isize(result, this), dest)?; + this.write_scalar(result, dest)?; } "pwrite" => { let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -123,7 +123,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); let result = this.write(fd, buf, count, Some(offset))?; // Now, `result` is the value we return back to the program. - this.write_scalar(Scalar::from_target_isize(result, this), dest)?; + this.write_scalar(result, dest)?; } "pread64" => { let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -132,7 +132,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let count = this.read_target_usize(count)?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?; let result = this.read(fd, buf, count, Some(offset))?; - this.write_scalar(Scalar::from_target_isize(result, this), dest)?; + this.write_scalar(result, dest)?; } "pwrite64" => { let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -143,7 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); let result = this.write(fd, buf, count, Some(offset))?; // Now, `result` is the value we return back to the program. - this.write_scalar(Scalar::from_target_isize(result, this), dest)?; + this.write_scalar(result, dest)?; } "close" => { let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -155,20 +155,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // in `this.fcntl()`, so we do not use `check_shim` here. this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?; let result = this.fcntl(args)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "dup" => { let [old_fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; let new_fd = this.dup(old_fd)?; - this.write_scalar(Scalar::from_i32(new_fd), dest)?; + this.write_scalar(new_fd, dest)?; } "dup2" => { let [old_fd, new_fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; let new_fd = this.read_scalar(new_fd)?.to_i32()?; let result = this.dup2(old_fd, new_fd)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; + } + "flock" => { + let [fd, op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let fd = this.read_scalar(fd)?.to_i32()?; + let op = this.read_scalar(op)?.to_i32()?; + let result = this.flock(fd, op)?; + this.write_scalar(result, dest)?; } // File and file system access @@ -176,32 +183,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `open` is variadic, the third argument is only present when the second argument has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?; let result = this.open(args)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "unlink" => { let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.unlink(path)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "symlink" => { let [target, linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.symlink(target, linkpath)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "rename" => { let [oldpath, newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.rename(oldpath, newpath)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "mkdir" => { let [path, mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.mkdir(path, mode)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "rmdir" => { let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.rmdir(path)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "opendir" => { let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -211,7 +218,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "closedir" => { let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.closedir(dirp)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "lseek64" => { let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -248,12 +255,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "fsync" => { let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.fsync(fd)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "fdatasync" => { let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.fdatasync(fd)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "readlink" => { let [pathname, buf, bufsize] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -278,7 +285,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "mkstemp" => { let [template] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.mkstemp(template)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } // Sockets @@ -294,7 +301,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "gettimeofday" => { let [tv, tz] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.gettimeofday(tv, tz)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "localtime_r" => { let [timep, result_op] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; @@ -466,23 +473,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "pthread_mutexattr_init" => { let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_mutexattr_init(attr)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_mutexattr_init(attr)?; + this.write_null(dest)?; } "pthread_mutexattr_settype" => { let [attr, kind] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pthread_mutexattr_settype(attr, kind)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "pthread_mutexattr_destroy" => { let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_mutexattr_destroy(attr)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_mutexattr_destroy(attr)?; + this.write_null(dest)?; } "pthread_mutex_init" => { let [mutex, attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_mutex_init(mutex, attr)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_mutex_init(mutex, attr)?; + this.write_null(dest)?; } "pthread_mutex_lock" => { let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -491,17 +498,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_mutex_trylock" => { let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pthread_mutex_trylock(mutex)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "pthread_mutex_unlock" => { let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pthread_mutex_unlock(mutex)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "pthread_mutex_destroy" => { let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_mutex_destroy(mutex)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_mutex_destroy(mutex)?; + this.write_int(0, dest)?; } "pthread_rwlock_rdlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -510,7 +517,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_rwlock_tryrdlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pthread_rwlock_tryrdlock(rwlock)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "pthread_rwlock_wrlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -519,22 +526,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_rwlock_trywrlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pthread_rwlock_trywrlock(rwlock)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "pthread_rwlock_unlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_rwlock_unlock(rwlock)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_rwlock_unlock(rwlock)?; + this.write_null(dest)?; } "pthread_rwlock_destroy" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_rwlock_destroy(rwlock)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_rwlock_destroy(rwlock)?; + this.write_null(dest)?; } "pthread_condattr_init" => { let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_init(attr)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_condattr_init(attr)?; + this.write_null(dest)?; } "pthread_condattr_setclock" => { let [attr, clock_id] = @@ -545,28 +552,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pthread_condattr_getclock" => { let [attr, clock_id] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_getclock(attr, clock_id)?; - this.write_scalar(result, dest)?; + this.pthread_condattr_getclock(attr, clock_id)?; + this.write_null(dest)?; } "pthread_condattr_destroy" => { let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_destroy(attr)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_condattr_destroy(attr)?; + this.write_null(dest)?; } "pthread_cond_init" => { let [cond, attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_cond_init(cond, attr)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_cond_init(cond, attr)?; + this.write_null(dest)?; } "pthread_cond_signal" => { let [cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_cond_signal(cond)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_cond_signal(cond)?; + this.write_null(dest)?; } "pthread_cond_broadcast" => { let [cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_cond_broadcast(cond)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_cond_broadcast(cond)?; + this.write_null(dest)?; } "pthread_cond_wait" => { let [cond, mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -578,25 +585,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_cond_destroy" => { let [cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_cond_destroy(cond)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_cond_destroy(cond)?; + this.write_null(dest)?; } // Threading "pthread_create" => { let [thread, attr, start, arg] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_create(thread, attr, start, arg)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_create(thread, attr, start, arg)?; + this.write_null(dest)?; } "pthread_join" => { let [thread, retval] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_join(thread, retval)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_join(thread, retval)?; + this.write_null(dest)?; } "pthread_detach" => { let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_detach(thread)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_detach(thread)?; + this.write_null(dest)?; } "pthread_self" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -605,13 +612,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "sched_yield" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.sched_yield()?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.sched_yield()?; + this.write_null(dest)?; } "nanosleep" => { let [req, rem] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.nanosleep(req, rem)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } "sched_getaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. @@ -640,23 +647,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(mask)? { let einval = this.eval_libc("EFAULT"); this.set_last_error(einval)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; + this.write_int(-1, dest)?; } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 { // we only copy whole chunks of size_of::() let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; + this.write_int(-1, dest)?; } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) { let cpuset = cpuset.clone(); // we only copy whole chunks of size_of::() let byte_count = Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap()); this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?; - this.write_scalar(Scalar::from_i32(0), dest)?; + this.write_null(dest)?; } else { // The thread whose ID is pid could not be found let einval = this.eval_libc("ESRCH"); this.set_last_error(einval)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; + this.write_int(-1, dest)?; } } "sched_setaffinity" => { @@ -683,7 +690,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(mask)? { let einval = this.eval_libc("EFAULT"); this.set_last_error(einval)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; + this.write_int(-1, dest)?; } else { // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`. // Any unspecified bytes are treated as zero here (none of the CPUs are configured). @@ -695,13 +702,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match CpuAffinityMask::from_array(this, this.machine.num_cpus, bits_array) { Some(cpuset) => { this.machine.thread_cpu_affinity.insert(thread_id, cpuset); - this.write_scalar(Scalar::from_i32(0), dest)?; + this.write_null(dest)?; } None => { // The intersection between the mask and the available CPUs was empty. let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; + this.write_int(-1, dest)?; } } } @@ -759,10 +766,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if bufsize > 256 { let err = this.eval_libc("EIO"); this.set_last_error(err)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; + this.write_int(-1, dest)?; } else { this.gen_random(buf, bufsize)?; - this.write_scalar(Scalar::from_i32(0), dest)?; + this.write_null(dest)?; } } "getrandom" => { diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 6923b39733f0b..d93374db8187f 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -16,6 +16,8 @@ use crate::shims::unix::*; use crate::*; use shims::time::system_time_to_duration; +use self::fd::FlockOp; + #[derive(Debug)] struct FileHandle { file: File, @@ -127,6 +129,97 @@ impl FileDescription for FileHandle { } } + fn flock<'tcx>( + &self, + communicate_allowed: bool, + op: FlockOp, + ) -> InterpResult<'tcx, io::Result<()>> { + assert!(communicate_allowed, "isolation should have prevented even opening a file"); + #[cfg(target_family = "unix")] + { + use std::os::fd::AsRawFd; + + use FlockOp::*; + // We always use non-blocking call to prevent interpreter from being blocked + let (host_op, lock_nb) = match op { + SharedLock { nonblocking } => (libc::LOCK_SH | libc::LOCK_NB, nonblocking), + ExclusiveLock { nonblocking } => (libc::LOCK_EX | libc::LOCK_NB, nonblocking), + Unlock => (libc::LOCK_UN, false), + }; + + let fd = self.file.as_raw_fd(); + let ret = unsafe { libc::flock(fd, host_op) }; + let res = match ret { + 0 => Ok(()), + -1 => { + let err = io::Error::last_os_error(); + if !lock_nb && err.kind() == io::ErrorKind::WouldBlock { + throw_unsup_format!("blocking `flock` is not currently supported"); + } + Err(err) + } + ret => panic!("Unexpected return value from flock: {ret}"), + }; + Ok(res) + } + + #[cfg(target_family = "windows")] + { + use std::os::windows::io::AsRawHandle; + use windows_sys::Win32::{ + Foundation::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE}, + Storage::FileSystem::{ + LockFileEx, UnlockFile, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, + }, + }; + let fh = self.file.as_raw_handle() as HANDLE; + + use FlockOp::*; + let (ret, lock_nb) = match op { + SharedLock { nonblocking } | ExclusiveLock { nonblocking } => { + // We always use non-blocking call to prevent interpreter from being blocked + let mut flags = LOCKFILE_FAIL_IMMEDIATELY; + if matches!(op, ExclusiveLock { .. }) { + flags |= LOCKFILE_EXCLUSIVE_LOCK; + } + let ret = unsafe { LockFileEx(fh, flags, 0, !0, !0, &mut std::mem::zeroed()) }; + (ret, nonblocking) + } + Unlock => { + let ret = unsafe { UnlockFile(fh, 0, 0, !0, !0) }; + (ret, false) + } + }; + + let res = match ret { + TRUE => Ok(()), + FALSE => { + let mut err = io::Error::last_os_error(); + let code: u32 = err.raw_os_error().unwrap().try_into().unwrap(); + if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) { + if lock_nb { + // The io error mapping does not know about these error codes, + // so we translate it to `WouldBlock` manually. + let desc = format!("LockFileEx wouldblock error: {err}"); + err = io::Error::new(io::ErrorKind::WouldBlock, desc); + } else { + throw_unsup_format!("blocking `flock` is not currently supported"); + } + } + Err(err) + } + _ => panic!("Unexpected return value: {ret}"), + }; + Ok(res) + } + + #[cfg(not(any(target_family = "unix", target_family = "windows")))] + { + let _ = op; + compile_error!("flock is supported only on UNIX and Windows hosts"); + } + } + fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.file.is_terminal() } @@ -302,7 +395,7 @@ fn maybe_sync_file( impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> { + fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> { if args.len() < 2 { throw_ub_format!( "incorrect number of arguments for `open`: got {}, expected at least 2", @@ -410,7 +503,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // if the flag contains `O_TMPFILE` then we return a graceful error let eopnotsupp = this.eval_libc("EOPNOTSUPP"); this.set_last_error(eopnotsupp)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } } @@ -431,7 +524,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if path.is_symlink() { let eloop = this.eval_libc("ELOOP"); this.set_last_error(eloop)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } } mirror |= o_nofollow; @@ -447,14 +540,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let fd = options .open(path) - .map(|file| this.machine.fds.insert_fd(FileHandle { file, writable })); + .map(|file| this.machine.fds.insert_new(FileHandle { file, writable })); - this.try_unwrap_io_result(fd) + Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?)) } fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { @@ -483,19 +576,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let communicate = this.machine.communicate(); - let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { + let Some(mut file_description) = this.machine.fds.get_mut(fd) else { return Ok(Scalar::from_i64(this.fd_not_found()?)); }; - let result = file_descriptor + let result = file_description .seek(communicate, seek_from)? .map(|offset| i64::try_from(offset).unwrap()); - drop(file_descriptor); + drop(file_description); let result = this.try_unwrap_io_result(result)?; Ok(Scalar::from_i64(result)) } - fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -504,18 +597,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let result = remove_file(path).map(|_| 0); - this.try_unwrap_io_result(result) + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn symlink( &mut self, target_op: &OpTy<'tcx>, linkpath_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, Scalar> { #[cfg(unix)] fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { std::os::unix::fs::symlink(src, dst) @@ -535,11 +628,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let result = create_link(&target, &linkpath).map(|_| 0); - this.try_unwrap_io_result(result) + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn macos_fbsd_stat( @@ -638,7 +731,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { flags_op: &OpTy<'tcx>, // Should be an `int` mask_op: &OpTy<'tcx>, // Should be an `unsigned int` statxbuf_op: &OpTy<'tcx>, // Should be a `struct statx *` - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("linux", "statx"); @@ -653,7 +746,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? { let efault = this.eval_libc("EFAULT"); this.set_last_error(efault)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?; @@ -695,7 +788,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.eval_libc("EBADF") }; this.set_last_error(ecode)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } // the `_mask_op` parameter specifies the file information that the caller requested. @@ -717,7 +810,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let metadata = match metadata { Some(metadata) => metadata, - None => return Ok(-1), + None => return Ok(Scalar::from_i32(-1)), }; // The `mode` field specifies the type of the file and the permissions over the file for @@ -810,14 +903,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.project_field_named(&statxbuf, "stx_mtime")?, )?; - Ok(0) + Ok(Scalar::from_i32(0)) } fn rename( &mut self, oldpath_op: &OpTy<'tcx>, newpath_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let oldpath_ptr = this.read_pointer(oldpath_op)?; @@ -826,7 +919,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? { let efault = this.eval_libc("EFAULT"); this.set_last_error(efault)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let oldpath = this.read_path_from_c_str(oldpath_ptr)?; @@ -836,15 +929,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let result = rename(oldpath, newpath).map(|_| 0); - this.try_unwrap_io_result(result) + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } - fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); #[cfg_attr(not(unix), allow(unused_variables))] @@ -860,7 +953,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } #[cfg_attr(not(unix), allow(unused_mut))] @@ -876,10 +969,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = builder.create(path).map(|_| 0i32); - this.try_unwrap_io_result(result) + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } - fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -888,12 +981,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } let result = remove_dir(path).map(|_| 0i32); - this.try_unwrap_io_result(result) + Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?)) } fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { @@ -996,7 +1089,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.ptr_to_mplace(entry, dirent64_layout), )?; - let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?; + let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this); this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?; Some(entry) @@ -1143,27 +1236,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { })) } - fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let dirp = this.read_target_usize(dirp_op)?; // Reject if isolation is enabled. - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + Ok(Scalar::from_i32(if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`closedir`", reject_with)?; - // Set error code as "EBADF" (bad fd) - return this.fd_not_found(); - } - - if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { + this.fd_not_found()? + } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { if let Some(entry) = open_dir.entry { this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; } drop(open_dir); - Ok(0) + 0 } else { - this.fd_not_found() - } + this.fd_not_found()? + })) } fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> { @@ -1176,30 +1266,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let Some(file_descriptor) = this.machine.fds.get(fd) else { + let Some(file_description) = this.machine.fds.get(fd) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // FIXME: Support ftruncate64 for all FDs let FileHandle { file, writable } = - file_descriptor.downcast_ref::().ok_or_else(|| { + file_description.downcast_ref::().ok_or_else(|| { err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") })?; if *writable { if let Ok(length) = length.try_into() { let result = file.set_len(length); - drop(file_descriptor); + drop(file_description); let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; Ok(Scalar::from_i32(result)) } else { - drop(file_descriptor); + drop(file_description); let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; Ok(Scalar::from_i32(-1)) } } else { - drop(file_descriptor); + drop(file_description); // The file is not writable let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; @@ -1207,7 +1297,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - fn fsync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn fsync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { // On macOS, `fsync` (unlike `fcntl(F_FULLFSYNC)`) does not wait for the // underlying disk to finish writing. In the interest of host compatibility, // we conservatively implement this with `sync_all`, which @@ -1221,28 +1311,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fsync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return this.fd_not_found(); + return Ok(Scalar::from_i32(this.fd_not_found()?)); } - return self.ffullsync_fd(fd); + self.ffullsync_fd(fd) } - fn ffullsync_fd(&mut self, fd: i32) -> InterpResult<'tcx, i32> { + fn ffullsync_fd(&mut self, fd: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let Some(file_descriptor) = this.machine.fds.get(fd) else { - return Ok(this.fd_not_found()?); + let Some(file_description) = this.machine.fds.get(fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = - file_descriptor.downcast_ref::().ok_or_else(|| { + file_description.downcast_ref::().ok_or_else(|| { err_unsup_format!("`fsync` is only supported on file-backed file descriptors") })?; let io_result = maybe_sync_file(file, *writable, File::sync_all); - drop(file_descriptor); - this.try_unwrap_io_result(io_result) + drop(file_description); + Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } - fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -1251,20 +1341,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fdatasync`", reject_with)?; // Set error code as "EBADF" (bad fd) - return this.fd_not_found(); + return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let Some(file_descriptor) = this.machine.fds.get(fd) else { - return Ok(this.fd_not_found()?); + let Some(file_description) = this.machine.fds.get(fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = - file_descriptor.downcast_ref::().ok_or_else(|| { + file_description.downcast_ref::().ok_or_else(|| { err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors") })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); - drop(file_descriptor); - this.try_unwrap_io_result(io_result) + drop(file_description); + Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } fn sync_file_range( @@ -1302,18 +1392,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - let Some(file_descriptor) = this.machine.fds.get(fd) else { + let Some(file_description) = this.machine.fds.get(fd) else { return Ok(Scalar::from_i32(this.fd_not_found()?)); }; // Only regular files support synchronization. let FileHandle { file, writable } = - file_descriptor.downcast_ref::().ok_or_else(|| { + file_description.downcast_ref::().ok_or_else(|| { err_unsup_format!( "`sync_data_range` is only supported on file-backed file descriptors" ) })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); - drop(file_descriptor); + drop(file_description); Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1441,7 +1531,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } - fn mkstemp(&mut self, template_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn mkstemp(&mut self, template_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { use rand::seq::SliceRandom; // POSIX defines the template string. @@ -1472,7 +1562,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.reject_in_isolation("`mkstemp`", reject_with)?; let eacc = this.eval_libc("EACCES"); this.set_last_error(eacc)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } // Get the bytes of the suffix we expect in _target_ encoding. @@ -1490,7 +1580,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if last_six_char_bytes != suffix_bytes { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } // At this point we know we have 6 ASCII 'X' characters as a suffix. @@ -1544,8 +1634,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match file { Ok(f) => { - let fd = this.machine.fds.insert_fd(FileHandle { file: f, writable: true }); - return Ok(fd); + let fd = this.machine.fds.insert_new(FileHandle { file: f, writable: true }); + return Ok(Scalar::from_i32(fd)); } Err(e) => match e.kind() { @@ -1556,7 +1646,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // "On error, -1 is returned, and errno is set to // indicate the error" this.set_last_error_from_io_error(e)?; - return Ok(-1); + return Ok(Scalar::from_i32(-1)); } }, } @@ -1565,7 +1655,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We ran out of attempts to create the file, return an error. let eexist = this.eval_libc("EEXIST"); this.set_last_error(eexist)?; - Ok(-1) + Ok(Scalar::from_i32(-1)) } } @@ -1609,11 +1699,11 @@ impl FileMetadata { ecx: &mut MiriInterpCx<'tcx>, fd: i32, ) -> InterpResult<'tcx, Option> { - let Some(file_descriptor) = ecx.machine.fds.get(fd) else { + let Some(file_description) = ecx.machine.fds.get(fd) else { return ecx.fd_not_found().map(|_: i32| None); }; - let file = &file_descriptor + let file = &file_description .downcast_ref::() .ok_or_else(|| { err_unsup_format!( @@ -1623,7 +1713,7 @@ impl FileMetadata { .file; let metadata = file.metadata(); - drop(file_descriptor); + drop(file_description); FileMetadata::from_meta(ecx, metadata) } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index ad35d67876ccb..9127db3d00420 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -64,7 +64,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - let fd = this.machine.fds.insert_fd(Epoll::default()); + let fd = this.machine.fds.insert_new(Epoll::default()); Ok(Scalar::from_i32(fd)) } diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index 0fc28c72bb6fb..4ab8760d9306a 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -178,7 +178,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags); } - let fd = this.machine.fds.insert_fd(Event { + let fd = this.machine.fds.insert_new(Event { counter: val.into(), is_nonblock, clock: VClock::default(), diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 20c6a23479421..581f0db42e1dc 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -44,7 +44,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [dirfd, pathname, flags, mask, statxbuf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } // epoll, eventfd @@ -97,7 +97,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "gettid" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.linux_gettid()?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.write_scalar(result, dest)?; } // Dynamically invoked syscalls @@ -176,12 +176,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "__libc_current_sigrtmin" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_scalar(Scalar::from_i32(SIGRTMIN), dest)?; + this.write_int(SIGRTMIN, dest)?; } "__libc_current_sigrtmax" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?; + this.write_int(SIGRTMAX, dest)?; } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index c430eff0180ea..3b32612e8baf0 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -53,7 +53,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We just allocated this, the access is definitely in-bounds and fits into our address space. // mmap guarantees new mappings are zero-init. this.write_bytes_ptr( - ptr.offset(Size::from_bytes(old_size), this).unwrap().into(), + ptr.wrapping_offset(Size::from_bytes(old_size), this).into(), std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), ) .unwrap(); diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index cc0f932e03829..455820a9e6e5a 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -219,8 +219,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let fds = &mut this.machine.fds; - let sv0 = fds.insert_fd(socketpair_0); - let sv1 = fds.insert_fd(socketpair_1); + let sv0 = fds.insert_new(socketpair_0); + let sv1 = fds.insert_new(socketpair_1); let sv0 = Scalar::from_int(sv0, sv.layout.size); let sv1 = Scalar::from_int(sv1, sv.layout.size); diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index e8653117ae94b..0b889b1182248 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -363,20 +363,20 @@ fn cond_set_clock_id<'tcx>( impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"); mutexattr_set_kind(this, attr_op, default_kind)?; - Ok(0) + Ok(()) } fn pthread_mutexattr_settype( &mut self, attr_op: &OpTy<'tcx>, kind_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let kind = this.read_scalar(kind_op)?.to_i32()?; @@ -407,13 +407,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutexattr_set_kind(this, attr_op, kind)?; } else { let einval = this.eval_libc_i32("EINVAL"); - return Ok(einval); + return Ok(Scalar::from_i32(einval)); } - Ok(0) + Ok(Scalar::from_i32(0)) } - fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); // Destroying an uninit pthread_mutexattr is UB, so check to make sure it's not uninit. @@ -435,14 +435,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?, )?; - Ok(0) + Ok(()) } fn pthread_mutex_init( &mut self, mutex_op: &OpTy<'tcx>, attr_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let attr = this.read_pointer(attr_op)?; @@ -457,7 +457,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutex_set_kind(this, mutex_op, kind)?; - Ok(0) + Ok(()) } fn pthread_mutex_lock( @@ -501,25 +501,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(()) } - fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; let id = mutex_get_id(this, mutex_op)?; - if this.mutex_is_locked(id) { + Ok(Scalar::from_i32(if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); if owner_thread != this.active_thread() { - Ok(this.eval_libc_i32("EBUSY")) + this.eval_libc_i32("EBUSY") } else { if is_mutex_kind_default(this, kind)? || is_mutex_kind_normal(this, kind)? || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { - Ok(this.eval_libc_i32("EBUSY")) + this.eval_libc_i32("EBUSY") } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { this.mutex_lock(id); - Ok(0) + 0 } else { throw_unsup_format!( "called pthread_mutex_trylock on an unsupported type of mutex" @@ -529,11 +529,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { // The mutex is unlocked. Let's lock it. this.mutex_lock(id); - Ok(0) - } + 0 + })) } - fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; @@ -541,7 +541,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if let Some(_old_locked_count) = this.mutex_unlock(id)? { // The mutex was locked by the current thread. - Ok(0) + Ok(Scalar::from_i32(0)) } else { // The mutex was locked by another thread or not locked at all. See // the “Unlock When Not Owner” column in @@ -557,14 +557,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { - Ok(this.eval_libc_i32("EPERM")) + Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))) } else { throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex"); } } } - fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = mutex_get_id(this, mutex_op)?; @@ -583,7 +583,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // FIXME: delete interpreter state associated with this mutex. - Ok(0) + Ok(()) } fn pthread_rwlock_rdlock( @@ -605,16 +605,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(()) } - fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_write_locked(id) { - Ok(this.eval_libc_i32("EBUSY")) + Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { this.rwlock_reader_lock(id); - Ok(0) + Ok(Scalar::from_i32(0)) } } @@ -649,35 +649,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(()) } - fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_locked(id) { - Ok(this.eval_libc_i32("EBUSY")) + Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { this.rwlock_writer_lock(id); - Ok(0) + Ok(Scalar::from_i32(0)) } } - fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; #[allow(clippy::if_same_then_else)] - if this.rwlock_reader_unlock(id)? { - Ok(0) - } else if this.rwlock_writer_unlock(id)? { - Ok(0) + if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? { + Ok(()) } else { throw_ub_format!("unlocked an rwlock that was not locked by the active thread"); } } - fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; @@ -695,10 +693,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // FIXME: delete interpreter state associated with this rwlock. - Ok(0) + Ok(()) } - fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); // no clock attribute on macOS @@ -710,7 +708,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condattr_set_clock_id(this, attr_op, default_clock_id)?; } - Ok(0) + Ok(()) } fn pthread_condattr_setclock( @@ -737,16 +735,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, attr_op: &OpTy<'tcx>, clk_id_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?; - Ok(Scalar::from_i32(0)) + Ok(()) } - fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); // Destroying an uninit pthread_condattr is UB, so check to make sure it's not uninit. @@ -761,14 +759,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?, )?; - Ok(0) + Ok(()) } fn pthread_cond_init( &mut self, cond_op: &OpTy<'tcx>, attr_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let attr = this.read_pointer(attr_op)?; @@ -784,21 +782,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { cond_set_clock_id(this, cond_op, clock_id)?; - Ok(0) + Ok(()) } - fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; this.condvar_signal(id)?; - Ok(0) + Ok(()) } - fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; while this.condvar_signal(id)? {} - Ok(0) + Ok(()) } fn pthread_cond_wait( @@ -869,7 +867,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(()) } - fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; @@ -885,6 +883,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_uninit(&this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?; // FIXME: delete interpreter state associated with this condvar. - Ok(0) + Ok(()) } } diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 6fe331ba623a1..56e8270aa62ac 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -1,5 +1,4 @@ use crate::*; -use rustc_middle::ty::layout::LayoutOf; use rustc_target::spec::abi::Abi; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -10,7 +9,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _attr: &OpTy<'tcx>, start_routine: &OpTy<'tcx>, arg: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let thread_info_place = this.deref_pointer_as(thread, this.libc_ty_layout("pthread_t"))?; @@ -24,17 +23,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { start_routine, Abi::C { unwind: false }, func_arg, - this.layout_of(this.tcx.types.usize)?, + this.machine.layouts.mut_raw_ptr, )?; - Ok(0) + Ok(()) } - fn pthread_join( - &mut self, - thread: &OpTy<'tcx>, - retval: &OpTy<'tcx>, - ) -> InterpResult<'tcx, i32> { + fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); if !this.ptr_is_null(this.read_pointer(retval)?)? { @@ -45,10 +40,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?; - Ok(0) + Ok(()) } - fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { + fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; @@ -57,7 +52,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /*allow_terminated_joined*/ false, )?; - Ok(0) + Ok(()) } fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> { @@ -113,11 +108,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") }) } - fn sched_yield(&mut self) -> InterpResult<'tcx, i32> { + fn sched_yield(&mut self) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); this.yield_active_thread(); - Ok(0) + Ok(()) } } diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 77ae06bd5c2d8..495df18a6eaa2 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -197,11 +197,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } #[allow(non_snake_case)] - fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> { + fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentProcessId"); - Ok(this.get_pid()) + Ok(Scalar::from_u32(this.get_pid())) } #[allow(non_snake_case)] diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 71f6a2bc03333..f840ba161650e 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -141,7 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "GetCurrentProcessId" => { let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let result = this.GetCurrentProcessId()?; - this.write_int(result, dest)?; + this.write_scalar(result, dest)?; } // File related shims @@ -372,7 +372,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?; // Return success (`1`). - this.write_scalar(Scalar::from_i32(1), dest)?; + this.write_int(1, dest)?; } // Access to command-line arguments @@ -563,7 +563,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; - this.write_scalar(Scalar::from_i32(1), dest)?; + this.write_int(1, dest)?; } "BCryptGenRandom" => { // used by getrandom 0.2 @@ -627,7 +627,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.CloseHandle(handle)?; - this.write_scalar(Scalar::from_u32(1), dest)?; + this.write_int(1, dest)?; } "GetModuleFileNameW" => { let [handle, filename, size] = diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index d534fdab2914e..e94bef529521e 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" @@ -46,9 +46,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.0.96" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" [[package]] name = "cfg-if" @@ -58,12 +58,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -98,9 +98,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "hermit-abi" @@ -119,46 +119,47 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -166,13 +167,13 @@ name = "miri-test-deps" version = "0.1.0" dependencies = [ "getrandom 0.1.16", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "num_cpus", "page_size", "tempfile", "tokio", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -187,9 +188,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -218,9 +219,9 @@ checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -236,9 +237,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" @@ -250,7 +251,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -269,14 +270,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "syn" -version = "2.0.60" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -285,38 +286,38 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "libc", "mio", - "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -417,141 +418,75 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs index 96dd99e884428..39b1c3007cb00 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs @@ -1,11 +1,12 @@ //@ignore-target-windows: No pthreads on Windows +//~^ERROR: calling a function with more arguments than it expected //! The thread function must have exactly one argument. use std::{mem, ptr}; extern "C" fn thread_start() -> *mut libc::c_void { - panic!() //~ ERROR: callee has fewer arguments than expected + panic!() } fn main() { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr index ca6a05ac7dd43..aa67420c7538a 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr @@ -1,14 +1,10 @@ -error: Undefined Behavior: callee has fewer arguments than expected - --> $DIR/libc_pthread_create_too_few_args.rs:LL:CC - | -LL | panic!() - | ^^^^^^^^ callee has fewer arguments than expected +error: Undefined Behavior: calling a function with more arguments than it expected | + = note: calling a function with more arguments than it expected + = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE on thread `unnamed-ID`: - = note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs index d8fbc68d344cd..fc2ab71dff735 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs @@ -1,11 +1,12 @@ //@ignore-target-windows: No pthreads on Windows +//~^ERROR: calling a function with fewer arguments than it requires //! The thread function must have exactly one argument. use std::{mem, ptr}; extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void { - panic!() //~ ERROR: callee has more arguments than expected + panic!() } fn main() { diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr index 6ab48a7666673..4de947b1694bf 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr @@ -1,14 +1,10 @@ -error: Undefined Behavior: callee has more arguments than expected - --> $DIR/libc_pthread_create_too_many_args.rs:LL:CC - | -LL | panic!() - | ^^^^^^^^ callee has more arguments than expected +error: Undefined Behavior: calling a function with fewer arguments than it requires | + = note: calling a function with fewer arguments than it requires + = note: (no span available) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE on thread `unnamed-ID`: - = note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/libc/affinity.stderr b/src/tools/miri/tests/fail-dep/libc/affinity.stderr index b9f79fdda89c4..38414623ccb94 100644 --- a/src/tools/miri/tests/fail-dep/libc/affinity.stderr +++ b/src/tools/miri/tests/fail-dep/libc/affinity.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC and there are only 128 bytes starting at that pointer +error: Undefined Behavior: memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation --> $DIR/affinity.rs:LL:CC | LL | let err = unsafe { sched_setaffinity(PID, size_of::() + 1, &cpuset) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC and there are only 128 bytes starting at that pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 129 bytes of memory, but got ALLOC which is only 128 bytes from the end of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail-dep/tokio/sleep.stderr b/src/tools/miri/tests/fail-dep/tokio/sleep.stderr index 4b12729bbff9c..6d19faab90570 100644 --- a/src/tools/miri/tests/fail-dep/tokio/sleep.stderr +++ b/src/tools/miri/tests/fail-dep/tokio/sleep.stderr @@ -2,7 +2,7 @@ error: unsupported operation: returning ready events from epoll_wait is not yet --> CARGO_REGISTRY/.../epoll.rs:LL:CC | LL | / syscall!(epoll_wait( -LL | | self.ep, +LL | | self.ep.as_raw_fd(), LL | | events.as_mut_ptr(), LL | | events.capacity() as i32, LL | | timeout, diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr index 07bb3293989e7..64bbbfcd848b6 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.stack.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer +error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr index 07bb3293989e7..64bbbfcd848b6 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.tree.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer +error: Undefined Behavior: out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC and there are only 2 bytes starting at that pointer + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to 4 bytes of memory, but got ALLOC which is only 2 bytes from the end of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr index bdd245e184948..4bfac8f96578d 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_project.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer +error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation --> $DIR/out_of_bounds_project.rs:LL:CC | LL | let _field = addr_of!((*ptr).2); - | ^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer + | ^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs index fd667fbe454c1..6a7a26710d16b 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.rs @@ -9,7 +9,7 @@ fn main() { // Make sure we check the ABI when Miri itself invokes a function // as part of a shim implementation. std::intrinsics::catch_unwind( - //~^ ERROR: calling a function with ABI C using caller ABI Rust + //~^ ERROR: calling a function with calling convention C using calling convention Rust std::mem::transmute::(try_fn), std::ptr::null_mut(), |_, _| unreachable!(), diff --git a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr index 501f17c86d69c..890fed09e4819 100644 --- a/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr +++ b/src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with ABI C using caller ABI Rust +error: Undefined Behavior: calling a function with calling convention C using calling convention Rust --> $DIR/check_callback_abi.rs:LL:CC | LL | / std::intrinsics::catch_unwind( @@ -7,7 +7,7 @@ LL | | std::mem::transmute::(try_fn), LL | | std::ptr::null_mut(), LL | | |_, _| unreachable!(), LL | | ); - | |_________^ calling a function with ABI C using caller ABI Rust + | |_________^ calling a function with calling convention C using calling convention Rust | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_2.rs b/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_2.rs deleted file mode 100644 index 0d4eea9a5bdea..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_2.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - let v = [0i8; 4]; - let x = &v as *const i8; - let x = unsafe { x.offset(isize::MIN) }; //~ERROR: overflowing in-bounds pointer arithmetic - panic!("this should never print: {:?}", x); -} diff --git a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_2.stderr b/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_2.stderr deleted file mode 100644 index 97fa1f19af106..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: overflowing in-bounds pointer arithmetic - --> $DIR/out_of_bounds_ptr_2.rs:LL:CC - | -LL | let x = unsafe { x.offset(isize::MIN) }; - | ^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/out_of_bounds_ptr_2.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr index 649075dbc55de..bf36c54ac781c 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: out-of-bounds `offset_from`: expected a pointer to 1 byte of memory, but got 0xa[noalloc] which is a dangling pointer (it has no provenance) +error: Undefined Behavior: out-of-bounds `offset_from` origin: expected a pointer to the end of 1 byte of memory, but got 0xb[noalloc] which is a dangling pointer (it has no provenance) --> $DIR/ptr_offset_from_different_ints.rs:LL:CC | LL | let _ = p1.byte_offset_from(p2); - | ^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: expected a pointer to 1 byte of memory, but got 0xa[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of 1 byte of memory, but got 0xb[noalloc] which is a dangling pointer (it has no provenance) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs index c4b6f69dd2be6..29bf61e25c896 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -//@normalize-stderr-test: "to \d+ bytes of memory" -> "to $$BYTES bytes of memory" +//@normalize-stderr-test: "\d+ bytes" -> "$$BYTES bytes" fn main() { let ptr = Box::into_raw(Box::new(0u32)); diff --git a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_1.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.rs similarity index 72% rename from src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_1.rs rename to src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.rs index f337090aa1e36..905fc678f6d50 100644 --- a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_1.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.rs @@ -1,7 +1,6 @@ fn main() { let v = [0i8; 4]; let x = &v as *const i8; - // The error is inside another function, so we cannot match it by line let x = unsafe { x.offset(5) }; //~ERROR: expected a pointer to 5 bytes of memory panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_1.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr similarity index 64% rename from src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_1.stderr rename to src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr index ddc5ae8efbc01..c4548200f0596 100644 --- a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_1.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds.stderr @@ -1,18 +1,18 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer - --> $DIR/out_of_bounds_ptr_1.rs:LL:CC +error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation + --> $DIR/ptr_offset_out_of_bounds.rs:LL:CC | LL | let x = unsafe { x.offset(5) }; - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC and there are only 4 bytes starting at that pointer + | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 5 bytes of memory, but got ALLOC which is only 4 bytes from the end of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information help: ALLOC was allocated here: - --> $DIR/out_of_bounds_ptr_1.rs:LL:CC + --> $DIR/ptr_offset_out_of_bounds.rs:LL:CC | LL | let v = [0i8; 4]; | ^ = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/out_of_bounds_ptr_1.rs:LL:CC + = note: inside `main` at $DIR/ptr_offset_out_of_bounds.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_3.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs similarity index 84% rename from src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_3.rs rename to src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs index fc9fb3d35d610..bd1d5c064c065 100644 --- a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_3.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.rs @@ -1,6 +1,6 @@ fn main() { let v = [0i8; 4]; let x = &v as *const i8; - let x = unsafe { x.offset(-1) }; //~ERROR: expected a pointer to 1 byte of memory + let x = unsafe { x.offset(-1) }; //~ERROR: expected a pointer to the end of 1 byte of memory panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_3.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr similarity index 63% rename from src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_3.stderr rename to src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr index 88963e712f4cc..8041e1542c68d 100644 --- a/src/tools/miri/tests/fail/intrinsics/out_of_bounds_ptr_3.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg.stderr @@ -1,18 +1,18 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC-0x1 which points to before the beginning of the allocation - --> $DIR/out_of_bounds_ptr_3.rs:LL:CC +error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC which is at the beginning of the allocation + --> $DIR/ptr_offset_out_of_bounds_neg.rs:LL:CC | LL | let x = unsafe { x.offset(-1) }; - | ^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC-0x1 which points to before the beginning of the allocation + | ^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC which is at the beginning of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information help: ALLOC was allocated here: - --> $DIR/out_of_bounds_ptr_3.rs:LL:CC + --> $DIR/ptr_offset_out_of_bounds_neg.rs:LL:CC | LL | let v = [0i8; 4]; | ^ = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/out_of_bounds_ptr_3.rs:LL:CC + = note: inside `main` at $DIR/ptr_offset_out_of_bounds_neg.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs index c3db1e23b9bfb..6839431223270 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.rs @@ -1,5 +1,8 @@ +//@normalize-stderr-test: "\d+ bytes" -> "$$BYTES bytes" + fn main() { - let v = [1i8, 2]; - let x = &v[1] as *const i8; - let _val = unsafe { x.offset(isize::MIN) }; //~ERROR: overflowing in-bounds pointer arithmetic + let v = [0i8; 4]; + let x = &v as *const i8; + let x = unsafe { x.offset(isize::MIN) }; //~ERROR: out-of-bounds pointer arithmetic + panic!("this should never print: {:?}", x); } diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr index 122529c30490a..ee5aebc6eaeef 100644 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr +++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_overflow.stderr @@ -1,12 +1,17 @@ -error: Undefined Behavior: overflowing in-bounds pointer arithmetic +error: Undefined Behavior: out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC which is at the beginning of the allocation --> $DIR/ptr_offset_overflow.rs:LL:CC | -LL | let _val = unsafe { x.offset(isize::MIN) }; - | ^^^^^^^^^^^^^^^^^^^^ overflowing in-bounds pointer arithmetic +LL | let x = unsafe { x.offset(isize::MIN) }; + | ^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC which is at the beginning of the allocation | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: +help: ALLOC was allocated here: + --> $DIR/ptr_offset_overflow.rs:LL:CC + | +LL | let v = [0i8; 4]; + | ^ + = note: BACKTRACE (of the first span): = note: inside `main` at $DIR/ptr_offset_overflow.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/intrinsics/raw_eq_on_ptr.rs b/src/tools/miri/tests/fail/intrinsics/raw_eq_on_ptr.rs deleted file mode 100644 index c14f86147dbb9..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/raw_eq_on_ptr.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(intrinsics)] - -extern "rust-intrinsic" { - fn raw_eq(a: &T, b: &T) -> bool; -} - -fn main() { - let x = &0; - unsafe { raw_eq(&x, &x) }; //~ERROR: `raw_eq` on bytes with provenance -} diff --git a/src/tools/miri/tests/fail/intrinsics/raw_eq_on_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/raw_eq_on_ptr.stderr deleted file mode 100644 index 83f81fa726ea9..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/raw_eq_on_ptr.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: `raw_eq` on bytes with provenance - --> $DIR/raw_eq_on_ptr.rs:LL:CC - | -LL | unsafe { raw_eq(&x, &x) }; - | ^^^^^^^^^^^^^^ `raw_eq` on bytes with provenance - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/raw_eq_on_ptr.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/miri_start_wrong_sig.rs b/src/tools/miri/tests/fail/miri_start_wrong_sig.rs new file mode 100644 index 0000000000000..dac83d817ba49 --- /dev/null +++ b/src/tools/miri/tests/fail/miri_start_wrong_sig.rs @@ -0,0 +1,21 @@ +//@compile-flags: -Cpanic=abort +//@error-in-other-file: `miri_start` must have the following signature: +#![no_main] +#![no_std] + +use core::fmt::Write; + +#[path = "../utils/mod.no_std.rs"] +mod utils; + +#[no_mangle] +fn miri_start() -> isize { + //~^ ERROR: mismatched types + writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap(); + 0 +} + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/tools/miri/tests/fail/miri_start_wrong_sig.stderr b/src/tools/miri/tests/fail/miri_start_wrong_sig.stderr new file mode 100644 index 0000000000000..6217191711658 --- /dev/null +++ b/src/tools/miri/tests/fail/miri_start_wrong_sig.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/miri_start_wrong_sig.rs:LL:CC + | +LL | fn miri_start() -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters + | + = note: expected signature `fn(isize, *const *const u8) -> _` + found signature `fn() -> _` + +error: `miri_start` must have the following signature: + fn miri_start(argc: isize, argv: *const *const u8) -> isize + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/tools/miri/tests/fail/no_main.rs b/src/tools/miri/tests/fail/no_main.rs index 01b8c7bd66bf0..4bbd820a636a0 100644 --- a/src/tools/miri/tests/fail/no_main.rs +++ b/src/tools/miri/tests/fail/no_main.rs @@ -1,2 +1,2 @@ -//@error-in-other-file: miri can only run programs that have a main function +//@error-in-other-file: Miri can only run programs that have a main function. #![no_main] diff --git a/src/tools/miri/tests/fail/no_main.stderr b/src/tools/miri/tests/fail/no_main.stderr index 1c4fc88989b21..e9b9e5d65b1d1 100644 --- a/src/tools/miri/tests/fail/no_main.stderr +++ b/src/tools/miri/tests/fail/no_main.stderr @@ -1,4 +1,11 @@ -error: miri can only run programs that have a main function +error: Miri can only run programs that have a main function. + Alternatively, you can export a `miri_start` function: + + #[cfg(miri)] + #[no_mangle] + fn miri_start(argc: isize, argv: *const *const u8) -> isize { + // Call the actual start function that your project implements, based on your target's conventions. + } error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/validity/recursive-validity-ref-bool.rs b/src/tools/miri/tests/fail/validity/recursive-validity-ref-bool.rs new file mode 100644 index 0000000000000..17b81f29cfe9a --- /dev/null +++ b/src/tools/miri/tests/fail/validity/recursive-validity-ref-bool.rs @@ -0,0 +1,8 @@ +//@compile-flags: -Zmiri-recursive-validation + +fn main() { + let x = 3u8; + let xref = &x; + let xref_wrong_type: &bool = unsafe { std::mem::transmute(xref) }; //~ERROR: encountered 0x03, but expected a boolean + let _val = *xref_wrong_type; +} diff --git a/src/tools/miri/tests/fail/validity/recursive-validity-ref-bool.stderr b/src/tools/miri/tests/fail/validity/recursive-validity-ref-bool.stderr new file mode 100644 index 0000000000000..2b2fa9b8a206d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/recursive-validity-ref-bool.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value at .: encountered 0x03, but expected a boolean + --> $DIR/recursive-validity-ref-bool.rs:LL:CC + | +LL | let xref_wrong_type: &bool = unsafe { std::mem::transmute(xref) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x03, but expected a boolean + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/recursive-validity-ref-bool.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs new file mode 100644 index 0000000000000..3f7f9b18be931 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs @@ -0,0 +1,70 @@ +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation + +use std::{fs::File, io::Error, os::fd::AsRawFd}; + +#[path = "../../utils/mod.rs"] +mod utils; + +fn main() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_shared_lock.txt", bytes); + + let files: Vec = (0..3).map(|_| File::open(&path).unwrap()).collect(); + + // Test that we can apply many shared locks + for file in files.iter() { + let fd = file.as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_SH) }; + if ret != 0 { + panic!("flock error: {}", Error::last_os_error()); + } + } + + // Test that shared lock prevents exclusive lock + { + let fd = files[0].as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) }; + assert_eq!(ret, -1); + let err = Error::last_os_error().raw_os_error().unwrap(); + assert_eq!(err, libc::EWOULDBLOCK); + } + + // Unlock shared lock + for file in files.iter() { + let fd = file.as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_UN) }; + if ret != 0 { + panic!("flock error: {}", Error::last_os_error()); + } + } + + // Take exclusive lock + { + let fd = files[0].as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_EX) }; + assert_eq!(ret, 0); + } + + // Test that shared lock prevents exclusive and shared locks + { + let fd = files[1].as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) }; + assert_eq!(ret, -1); + let err = Error::last_os_error().raw_os_error().unwrap(); + assert_eq!(err, libc::EWOULDBLOCK); + + let fd = files[2].as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB) }; + assert_eq!(ret, -1); + let err = Error::last_os_error().raw_os_error().unwrap(); + assert_eq!(err, libc::EWOULDBLOCK); + } + + // Unlock exclusive lock + { + let fd = files[0].as_raw_fd(); + let ret = unsafe { libc::flock(fd, libc::LOCK_UN) }; + assert_eq!(ret, 0); + } +} diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs index a226783155b42..40b8e23a33c47 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.rs +++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs @@ -1,7 +1,7 @@ #![feature(start)] #![no_std] -//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort -//@normalize-stderr-test: "id 20" -> "id $$ALLOC" +//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort +//@normalize-stderr-test: "id 21" -> "id $$ALLOC" //@only-target-linux: alloc IDs differ between OSes (due to extern static allocations) extern "Rust" { diff --git a/src/tools/miri/tests/pass/miri_start.rs b/src/tools/miri/tests/pass/miri_start.rs new file mode 100644 index 0000000000000..756a1f60be151 --- /dev/null +++ b/src/tools/miri/tests/pass/miri_start.rs @@ -0,0 +1,19 @@ +//@compile-flags: -Cpanic=abort +#![no_main] +#![no_std] + +use core::fmt::Write; + +#[path = "../utils/mod.no_std.rs"] +mod utils; + +#[no_mangle] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap(); + 0 +} + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/tools/miri/tests/pass/miri_start.stdout b/src/tools/miri/tests/pass/miri_start.stdout new file mode 100644 index 0000000000000..1c9e8489b575c --- /dev/null +++ b/src/tools/miri/tests/pass/miri_start.stdout @@ -0,0 +1 @@ +Hello from miri_start! diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 3a8b098ccbe36..95f8b05410293 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -73,7 +73,7 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> stdout_filters: stdout_filters().into(), mode, program, - out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("ui"), + out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri_ui"), edition: Some("2021".into()), // keep in sync with `./miri run` threads: std::env::var("MIRI_TEST_THREADS") .ok() diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 88e8640d56abb..d34f8ad052086 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -10,7 +10,7 @@ log = "0.4" anyhow = { version = "1", features = ["backtrace"] } humantime = "2" humansize = "2" -sysinfo = { version = "0.30", default-features = false } +sysinfo = { version = "0.31.2", default-features = false, features = ["disk"] } fs_extra = "1" camino = "1" tar = "0.4" diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index f6bf3b99ab293..30c9648bc08e9 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -60,17 +60,26 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> { .join(format!("llvm-config{}", executable_extension())); assert!(llvm_config.is_file()); - let rustc = format!("build.rustc={}", rustc_path.to_string().replace('\\', "/")); - let cargo = format!("build.cargo={}", cargo_path.to_string().replace('\\', "/")); - let llvm_config = - format!("target.{host_triple}.llvm-config={}", llvm_config.to_string().replace('\\', "/")); + let config_content = format!( + r#"profile = "user" +change-id = 115898 - log::info!("Set the following configurations for running tests:"); - log::info!("\t{rustc}"); - log::info!("\t{cargo}"); - log::info!("\t{llvm_config}"); +[build] +rustc = "{rustc}" +cargo = "{cargo}" + +[target.{host_triple}] +llvm-config = "{llvm_config}" +"#, + rustc = rustc_path.to_string().replace('\\', "/"), + cargo = cargo_path.to_string().replace('\\', "/"), + llvm_config = llvm_config.to_string().replace('\\', "/") + ); + log::info!("Using following `config.toml` for running tests:\n{config_content}"); // Simulate a stage 0 compiler with the extracted optimized dist artifacts. + std::fs::write("config.toml", config_content)?; + let x_py = env.checkout_path().join("x.py"); let mut args = vec![ env.python_binary(), @@ -89,12 +98,6 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> { "tests/run-pass-valgrind", "tests/ui", "tests/crashes", - "--set", - &rustc, - "--set", - &cargo, - "--set", - &llvm_config, ]; for test_path in env.skipped_tests() { args.extend(["--skip", test_path]); diff --git a/src/tools/run-make-support/src/artifact_names.rs b/src/tools/run-make-support/src/artifact_names.rs index bc6ec7566e555..0d7b5cb98388e 100644 --- a/src/tools/run-make-support/src/artifact_names.rs +++ b/src/tools/run-make-support/src/artifact_names.rs @@ -38,6 +38,17 @@ pub fn dynamic_lib_name(name: &str) -> String { format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION) } +/// Construct the name of the import library for the dynamic library, exclusive to MSVC and +/// accepted by link.exe. +#[track_caller] +#[must_use] +pub fn msvc_import_dynamic_lib_name(name: &str) -> String { + assert!(is_msvc(), "this function is exclusive to MSVC"); + assert!(!name.contains(char::is_whitespace), "import library name cannot contain whitespace"); + + format!("{name}.dll.lib") +} + /// Construct the dynamic library extension based on the target. #[must_use] pub fn dynamic_lib_extension() -> &'static str { diff --git a/src/tools/run-make-support/src/assertion_helpers.rs b/src/tools/run-make-support/src/assertion_helpers.rs index 4b5b349431db9..6d256fc594d8e 100644 --- a/src/tools/run-make-support/src/assertion_helpers.rs +++ b/src/tools/run-make-support/src/assertion_helpers.rs @@ -3,7 +3,7 @@ use std::panic; use std::path::Path; -use crate::fs; +use crate::{fs, regex}; /// Assert that `actual` is equal to `expected`. #[track_caller] @@ -47,6 +47,36 @@ pub fn assert_not_contains, N: AsRef>(haystack: H, needle: N) } } +/// Assert that `haystack` contains the regex pattern `needle`. +#[track_caller] +pub fn assert_contains_regex, N: AsRef>(haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + let re = regex::Regex::new(needle).unwrap(); + if !re.is_match(haystack) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was not found in haystack"); + } +} + +/// Assert that `haystack` does not contain the regex pattern `needle`. +#[track_caller] +pub fn assert_not_contains_regex, N: AsRef>(haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + let re = regex::Regex::new(needle).unwrap(); + if re.is_match(haystack) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was unexpectedly found in haystack"); + } +} + /// Assert that all files in `dir1` exist and have the same content in `dir2` pub fn assert_dirs_are_equal(dir1: impl AsRef, dir2: impl AsRef) { let dir2 = dir2.as_ref(); diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index fb94ff996f0d6..a0b96e25a0cba 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -7,7 +7,10 @@ use std::{ffi, panic}; use build_helper::drop_bomb::DropBomb; use crate::util::handle_failed_output; -use crate::{assert_contains, assert_equals, assert_not_contains}; +use crate::{ + assert_contains, assert_contains_regex, assert_equals, assert_not_contains, + assert_not_contains_regex, +}; /// This is a custom command wrapper that simplifies working with commands and makes it easier to /// ensure that we check the exit status of executed processes. @@ -163,11 +166,19 @@ pub struct CompletedProcess { impl CompletedProcess { #[must_use] + #[track_caller] pub fn stdout_utf8(&self) -> String { String::from_utf8(self.output.stdout.clone()).expect("stdout is not valid UTF-8") } #[must_use] + #[track_caller] + pub fn invalid_stdout_utf8(&self) -> String { + String::from_utf8_lossy(&self.output.stdout.clone()).to_string() + } + + #[must_use] + #[track_caller] pub fn stderr_utf8(&self) -> String { String::from_utf8(self.output.stderr.clone()).expect("stderr is not valid UTF-8") } @@ -191,6 +202,13 @@ impl CompletedProcess { self } + /// Checks that `stdout` does not contain the regex pattern `unexpected`. + #[track_caller] + pub fn assert_stdout_not_contains_regex>(&self, unexpected: S) -> &Self { + assert_not_contains_regex(&self.stdout_utf8(), unexpected); + self + } + /// Checks that `stdout` contains `expected`. #[track_caller] pub fn assert_stdout_contains>(&self, expected: S) -> &Self { @@ -198,6 +216,13 @@ impl CompletedProcess { self } + /// Checks that `stdout` contains the regex pattern `expected`. + #[track_caller] + pub fn assert_stdout_contains_regex>(&self, expected: S) -> &Self { + assert_contains_regex(&self.stdout_utf8(), expected); + self + } + /// Checks that trimmed `stderr` matches trimmed `expected`. #[track_caller] pub fn assert_stderr_equals>(&self, expected: S) -> &Self { @@ -212,10 +237,24 @@ impl CompletedProcess { self } + /// Checks that `stderr` contains the regex pattern `expected`. + #[track_caller] + pub fn assert_stderr_contains_regex>(&self, expected: S) -> &Self { + assert_contains_regex(&self.stderr_utf8(), expected); + self + } + /// Checks that `stderr` does not contain `unexpected`. #[track_caller] pub fn assert_stderr_not_contains>(&self, unexpected: S) -> &Self { - assert_not_contains(&self.stdout_utf8(), unexpected); + assert_not_contains(&self.stderr_utf8(), unexpected); + self + } + + /// Checks that `stderr` does not contain the regex pattern `unexpected`. + #[track_caller] + pub fn assert_stderr_not_contains_regex>(&self, unexpected: S) -> &Self { + assert_not_contains_regex(&self.stdout_utf8(), unexpected); self } diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs index f52524e7d54cd..e6460fb93d3e9 100644 --- a/src/tools/run-make-support/src/env.rs +++ b/src/tools/run-make-support/src/env.rs @@ -17,3 +17,10 @@ pub fn env_var_os(name: &str) -> OsString { None => panic!("failed to retrieve environment variable {name:?}"), } } + +/// Check if `NO_DEBUG_ASSERTIONS` is set (usually this may be set in CI jobs). +#[track_caller] +#[must_use] +pub fn no_debug_assertions() -> bool { + std::env::var_os("NO_DEBUG_ASSERTIONS").is_some() +} diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs index 86c9c08183197..f8d1666adda45 100644 --- a/src/tools/run-make-support/src/external_deps/c_build.rs +++ b/src/tools/run-make-support/src/external_deps/c_build.rs @@ -1,8 +1,7 @@ use std::path::PathBuf; -use super::cygpath::get_windows_path; use crate::artifact_names::{dynamic_lib_name, static_lib_name}; -use crate::external_deps::cc::cc; +use crate::external_deps::cc::{cc, cxx}; use crate::external_deps::llvm::llvm_ar; use crate::path_helpers::path; use crate::targets::{is_darwin, is_msvc, is_windows}; @@ -10,16 +9,34 @@ use crate::targets::{is_darwin, is_msvc, is_windows}; // FIXME(Oneirical): These native build functions should take a Path-based generic. /// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C file. #[track_caller] pub fn build_native_static_lib(lib_name: &str) -> PathBuf { + build_native_static_lib_internal(lib_name, false) +} + +/// Builds an optimized static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C file. +#[track_caller] +pub fn build_native_static_lib_optimized(lib_name: &str) -> PathBuf { + build_native_static_lib_internal(lib_name, true) +} + +#[track_caller] +fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf { let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; let src = format!("{lib_name}.c"); let lib_path = static_lib_name(lib_name); - if is_msvc() { - cc().arg("-c").out_exe(&obj_file).input(src).run(); - } else { - cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); - }; + + let mut cc = cc(); + if !is_msvc() { + cc.arg("-v"); + } + if optimzed { + cc.optimize(); + } + cc.arg("-c").out_exe(&obj_file).input(src).optimize().run(); + let obj_file = if is_msvc() { PathBuf::from(format!("{lib_name}.obj")) } else { @@ -43,8 +60,7 @@ pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf { }; let obj_file = if is_msvc() { format!("{lib_name}.obj") } else { format!("{lib_name}.o") }; if is_msvc() { - let mut out_arg = "-out:".to_owned(); - out_arg.push_str(&get_windows_path(&lib_path)); + let out_arg = format!("-out:{lib_path}"); cc().input(&obj_file).args(&["-link", "-dll", &out_arg]).run(); } else if is_darwin() { cc().out_exe(&lib_path).input(&obj_file).args(&["-dynamiclib", "-Wl,-dylib"]).run(); @@ -58,3 +74,24 @@ pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf { } path(lib_path) } + +/// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C++ file. +#[track_caller] +pub fn build_native_static_lib_cxx(lib_name: &str) -> PathBuf { + let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; + let src = format!("{lib_name}.cpp"); + let lib_path = static_lib_name(lib_name); + if is_msvc() { + cxx().arg("-EHs").arg("-c").out_exe(&obj_file).input(src).run(); + } else { + cxx().arg("-c").out_exe(&obj_file).input(src).run(); + }; + let obj_file = if is_msvc() { + PathBuf::from(format!("{lib_name}.obj")) + } else { + PathBuf::from(format!("{lib_name}.o")) + }; + llvm_ar().obj_to_ar().output_input(&lib_path, &obj_file).run(); + path(lib_path) +} diff --git a/src/tools/run-make-support/src/external_deps/cc.rs b/src/tools/run-make-support/src/external_deps/cc.rs index 39ac3efef3948..011ad89e170a6 100644 --- a/src/tools/run-make-support/src/external_deps/cc.rs +++ b/src/tools/run-make-support/src/external_deps/cc.rs @@ -1,7 +1,5 @@ use std::path::Path; -// FIXME(jieyouxu): can we get rid of the `cygpath` external dependency? -use super::cygpath::get_windows_path; use crate::command::Command; use crate::{env_var, is_msvc, is_windows, uname}; @@ -97,12 +95,12 @@ impl Cc { if is_msvc() { path.set_extension("exe"); - let fe_path = get_windows_path(&path); + let fe_path = path.clone(); path.set_extension(""); path.set_extension("obj"); - let fo_path = get_windows_path(path); - self.cmd.arg(format!("-Fe:{fe_path}")); - self.cmd.arg(format!("-Fo:{fo_path}")); + let fo_path = path; + self.cmd.arg(format!("-Fe:{}", fe_path.to_str().unwrap())); + self.cmd.arg(format!("-Fo:{}", fo_path.to_str().unwrap())); } else { self.cmd.arg("-o"); self.cmd.arg(name); @@ -117,6 +115,17 @@ impl Cc { self.cmd.arg(path.as_ref()); self } + + /// Optimize the output. + /// Equivalent to `-O3` for GNU-compatible linkers or `-O2` for MSVC linkers. + pub fn optimize(&mut self) -> &mut Self { + if is_msvc() { + self.cmd.arg("-O2"); + } else { + self.cmd.arg("-O3"); + } + self + } } /// `EXTRACFLAGS` diff --git a/src/tools/run-make-support/src/external_deps/clang.rs b/src/tools/run-make-support/src/external_deps/clang.rs index 2b0712541cd5f..2d110c6825e05 100644 --- a/src/tools/run-make-support/src/external_deps/clang.rs +++ b/src/tools/run-make-support/src/external_deps/clang.rs @@ -1,7 +1,7 @@ use std::path::Path; use crate::command::Command; -use crate::{bin_name, env_var}; +use crate::{bin_name, cwd, env_var}; /// Construct a new `clang` invocation. `clang` is not always available for all targets. #[track_caller] @@ -23,7 +23,8 @@ impl Clang { #[track_caller] pub fn new() -> Self { let clang = env_var("CLANG"); - let cmd = Command::new(clang); + let mut cmd = Command::new(clang); + cmd.arg("-L").arg(cwd()); Self { cmd } } diff --git a/src/tools/run-make-support/src/external_deps/cygpath.rs b/src/tools/run-make-support/src/external_deps/cygpath.rs deleted file mode 100644 index 07d8e840a63a8..0000000000000 --- a/src/tools/run-make-support/src/external_deps/cygpath.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::panic; -use std::path::Path; - -use crate::command::Command; -use crate::util::handle_failed_output; - -/// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is -/// available on the platform! -/// -/// # FIXME -/// -/// FIXME(jieyouxu): we should consider not depending on `cygpath`. -/// -/// > The cygpath program is a utility that converts Windows native filenames to Cygwin POSIX-style -/// > pathnames and vice versa. -/// > -/// > [irrelevant entries omitted...] -/// > -/// > `-w, --windows print Windows form of NAMEs (C:\WINNT)` -/// > -/// > -- *from [cygpath documentation](https://cygwin.com/cygwin-ug-net/cygpath.html)*. -#[track_caller] -#[must_use] -pub fn get_windows_path>(path: P) -> String { - let caller = panic::Location::caller(); - let mut cygpath = Command::new("cygpath"); - cygpath.arg("-w"); - cygpath.arg(path.as_ref()); - let output = cygpath.run(); - if !output.status().success() { - handle_failed_output(&cygpath, output, caller.line()); - } - // cygpath -w can attach a newline - output.stdout_utf8().trim().to_string() -} diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs index b116bd08e3a6c..dc651fdd8205a 100644 --- a/src/tools/run-make-support/src/external_deps/llvm.rs +++ b/src/tools/run-make-support/src/external_deps/llvm.rs @@ -36,6 +36,18 @@ pub fn llvm_ar() -> LlvmAr { LlvmAr::new() } +/// Construct a new `llvm-nm` invocation. This assumes that `llvm-nm` is available +/// at `$LLVM_BIN_DIR/llvm-nm`. +pub fn llvm_nm() -> LlvmNm { + LlvmNm::new() +} + +/// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available +/// at `$LLVM_BIN_DIR/llvm-bcanalyzer`. +pub fn llvm_bcanalyzer() -> LlvmBcanalyzer { + LlvmBcanalyzer::new() +} + /// A `llvm-readobj` invocation builder. #[derive(Debug)] #[must_use] @@ -71,11 +83,27 @@ pub struct LlvmAr { cmd: Command, } +/// A `llvm-nm` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmNm { + cmd: Command, +} + +/// A `llvm-bcanalyzer` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmBcanalyzer { + cmd: Command, +} + crate::macros::impl_common_helpers!(LlvmReadobj); crate::macros::impl_common_helpers!(LlvmProfdata); crate::macros::impl_common_helpers!(LlvmFilecheck); crate::macros::impl_common_helpers!(LlvmObjdump); crate::macros::impl_common_helpers!(LlvmAr); +crate::macros::impl_common_helpers!(LlvmNm); +crate::macros::impl_common_helpers!(LlvmBcanalyzer); /// Generate the path to the bin directory of LLVM. #[must_use] @@ -123,7 +151,8 @@ impl LlvmReadobj { self } - /// Pass `--symbols` to display the symbol. + /// Pass `--symbols` to display the symbol table, including both local + /// and global symbols. pub fn symbols(&mut self) -> &mut Self { self.cmd.arg("--symbols"); self @@ -218,6 +247,12 @@ impl LlvmObjdump { self.cmd.arg(path.as_ref()); self } + + /// Disassemble all executable sections found in the input files. + pub fn disassemble(&mut self) -> &mut Self { + self.cmd.arg("-d"); + self + } } impl LlvmAr { @@ -236,6 +271,12 @@ impl LlvmAr { self } + /// Extract archive members back to files. + pub fn extract(&mut self) -> &mut Self { + self.cmd.arg("x"); + self + } + /// Provide an output, then an input file. Bundled in one function, as llvm-ar has /// no "--output"-style flag. pub fn output_input(&mut self, out: impl AsRef, input: impl AsRef) -> &mut Self { @@ -244,3 +285,35 @@ impl LlvmAr { self } } + +impl LlvmNm { + /// Construct a new `llvm-nm` invocation. This assumes that `llvm-nm` is available + /// at `$LLVM_BIN_DIR/llvm-nm`. + pub fn new() -> Self { + let llvm_nm = llvm_bin_dir().join("llvm-nm"); + let cmd = Command::new(llvm_nm); + Self { cmd } + } + + /// Provide an input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } +} + +impl LlvmBcanalyzer { + /// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available + /// at `$LLVM_BIN_DIR/llvm-bcanalyzer`. + pub fn new() -> Self { + let llvm_bcanalyzer = llvm_bin_dir().join("llvm-bcanalyzer"); + let cmd = Command::new(llvm_bcanalyzer); + Self { cmd } + } + + /// Provide an input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } +} diff --git a/src/tools/run-make-support/src/external_deps/mod.rs b/src/tools/run-make-support/src/external_deps/mod.rs index a2dc426f3f229..f7c84724d0e07 100644 --- a/src/tools/run-make-support/src/external_deps/mod.rs +++ b/src/tools/run-make-support/src/external_deps/mod.rs @@ -9,6 +9,3 @@ pub mod llvm; pub mod python; pub mod rustc; pub mod rustdoc; - -// Library-internal external dependency. -mod cygpath; diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 71d28dd9675fb..cece58d29566c 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -5,6 +5,7 @@ use crate::command::Command; use crate::env::env_var; use crate::path_helpers::cwd; use crate::util::set_host_rpath; +use crate::{is_darwin, is_msvc, is_windows, uname}; /// Construct a new `rustc` invocation. This will automatically set the library /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this. @@ -314,4 +315,62 @@ impl Rustc { self.cmd.arg(format!("-Clinker-flavor={linker_flavor}")); self } + + /// `EXTRARSCXXFLAGS` + pub fn extra_rs_cxx_flags(&mut self) -> &mut Self { + // Adapted from tools.mk (trimmed): + // + // ```makefile + // ifdef IS_WINDOWS + // ifdef IS_MSVC + // else + // EXTRARSCXXFLAGS := -lstatic:-bundle=stdc++ + // endif + // else + // ifeq ($(UNAME),Darwin) + // EXTRARSCXXFLAGS := -lc++ + // else + // ifeq ($(UNAME),FreeBSD) + // else + // ifeq ($(UNAME),SunOS) + // else + // ifeq ($(UNAME),OpenBSD) + // else + // EXTRARSCXXFLAGS := -lstdc++ + // endif + // endif + // endif + // endif + // endif + // ``` + let flag = if is_windows() { + // So this is a bit hacky: we can't use the DLL version of libstdc++ because + // it pulls in the DLL version of libgcc, which means that we end up with 2 + // instances of the DW2 unwinding implementation. This is a problem on + // i686-pc-windows-gnu because each module (DLL/EXE) needs to register its + // unwind information with the unwinding implementation, and libstdc++'s + // __cxa_throw won't see the unwinding info we registered with our statically + // linked libgcc. + // + // Now, simply statically linking libstdc++ would fix this problem, except + // that it is compiled with the expectation that pthreads is dynamically + // linked as a DLL and will fail to link with a statically linked libpthread. + // + // So we end up with the following hack: we link use static:-bundle to only + // link the parts of libstdc++ that we actually use, which doesn't include + // the dependency on the pthreads DLL. + if is_msvc() { None } else { Some("-lstatic:-bundle=stdc++") } + } else if is_darwin() { + Some("-lc++") + } else { + match &uname()[..] { + "FreeBSD" | "SunOS" | "OpenBSD" => None, + _ => Some("-lstdc++"), + } + }; + if let Some(flag) = flag { + self.cmd.arg(flag); + } + self + } } diff --git a/src/tools/run-make-support/src/fs.rs b/src/tools/run-make-support/src/fs.rs index 0a79616163308..2c35ba52a629c 100644 --- a/src/tools/run-make-support/src/fs.rs +++ b/src/tools/run-make-support/src/fs.rs @@ -9,11 +9,19 @@ pub fn create_symlink, Q: AsRef>(original: P, link: Q) { if link.as_ref().exists() { std::fs::remove_dir(link.as_ref()).unwrap(); } - std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!( - "failed to create symlink {:?} for {:?}", - link.as_ref().display(), - original.as_ref().display(), - )); + if original.as_ref().is_file() { + std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!( + "failed to create symlink {:?} for {:?}", + link.as_ref().display(), + original.as_ref().display(), + )); + } else { + std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()).expect(&format!( + "failed to create symlink {:?} for {:?}", + link.as_ref().display(), + original.as_ref().display(), + )); + } } /// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. @@ -41,6 +49,8 @@ pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) { let ty = entry.file_type()?; if ty.is_dir() { copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?; + } else if ty.is_symlink() { + copy_symlink(entry.path(), dst.join(entry.file_name()))?; } else { std::fs::copy(entry.path(), dst.join(entry.file_name()))?; } @@ -59,6 +69,12 @@ pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) { } } +fn copy_symlink, Q: AsRef>(from: P, to: Q) -> io::Result<()> { + let target_path = std::fs::read_link(from).unwrap(); + create_symlink(target_path, to); + Ok(()) +} + /// Helper for reading entries in a given directory. pub fn read_dir_entries, F: FnMut(&Path)>(dir: P, mut callback: F) { for entry in read_dir(dir) { diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 085120764b463..a44dd00ad7986 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -21,6 +21,7 @@ pub mod run; pub mod scoped_run; pub mod string; pub mod targets; +pub mod symbols; // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs` // to tests to avoid colliding with commonly used `use std::fs;`. @@ -43,13 +44,13 @@ pub use wasmparser; pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc}; // These rely on external dependencies. -pub use c_build::{build_native_dynamic_lib, build_native_static_lib}; pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc}; +pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_optimized, build_native_static_lib_cxx}; pub use clang::{clang, Clang}; pub use htmldocck::htmldocck; pub use llvm::{ - llvm_ar, llvm_filecheck, llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmFilecheck, - LlvmObjdump, LlvmProfdata, LlvmReadobj, + llvm_ar, llvm_bcanalyzer, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj, + LlvmAr, LlvmBcanalyzer, LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj, }; pub use python::python_command; pub use rustc::{aux_build, bare_rustc, rustc, Rustc}; @@ -71,20 +72,22 @@ pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, targe /// Helpers for building names of output artifacts that are potentially target-specific. pub use artifact_names::{ - bin_name, dynamic_lib_extension, dynamic_lib_name, rust_lib_name, static_lib_name, + bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name, + static_lib_name, }; /// Path-related helpers. pub use path_helpers::{ - cwd, filename_not_in_denylist, has_extension, has_prefix, has_suffix, not_contains, path, - shallow_find_files, source_root, + cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, has_suffix, + not_contains, path, shallow_find_files, source_root, }; /// Helpers for scoped test execution where certain properties are attempted to be maintained. pub use scoped_run::{run_in_tmpdir, test_while_readonly}; pub use assertion_helpers::{ - assert_contains, assert_dirs_are_equal, assert_equals, assert_not_contains, + assert_contains, assert_contains_regex, assert_dirs_are_equal, assert_equals, + assert_not_contains, assert_not_contains_regex, }; pub use string::{ diff --git a/src/tools/run-make-support/src/path_helpers.rs b/src/tools/run-make-support/src/path_helpers.rs index f37ea8dfef8c6..b788bc6ef302c 100644 --- a/src/tools/run-make-support/src/path_helpers.rs +++ b/src/tools/run-make-support/src/path_helpers.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use crate::env::env_var; +use crate::rfs; /// Return the current working directory. /// @@ -40,7 +41,7 @@ pub fn shallow_find_files, F: Fn(&PathBuf) -> bool>( filter: F, ) -> Vec { let mut matching_files = Vec::new(); - for entry in std::fs::read_dir(path).unwrap() { + for entry in rfs::read_dir(path) { let entry = entry.expect("failed to read directory entry."); let path = entry.path(); @@ -78,3 +79,8 @@ pub fn has_extension>(path: P, extension: &str) -> bool { pub fn has_suffix>(path: P, suffix: &str) -> bool { path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix)) } + +/// Returns true if the filename at `path` contains `needle`. +pub fn filename_contains>(path: P, needle: &str) -> bool { + path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(needle)) +} diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs new file mode 100644 index 0000000000000..fd0c866bcc927 --- /dev/null +++ b/src/tools/run-make-support/src/symbols.rs @@ -0,0 +1,44 @@ +use std::path::Path; + +use object::{self, Object, ObjectSymbol, SymbolIterator}; + +/// Iterate through the symbols in an object file. +/// +/// Uses a callback because `SymbolIterator` does not own its data. +/// +/// Panics if `path` is not a valid object file readable by the current user. +pub fn with_symbol_iter(path: P, func: F) -> R +where + P: AsRef, + F: FnOnce(&mut SymbolIterator<'_, '_>) -> R, +{ + let raw_bytes = crate::fs::read(path); + let f = object::File::parse(raw_bytes.as_slice()).expect("unable to parse file"); + let mut iter = f.symbols(); + func(&mut iter) +} + +/// Check an object file's symbols for substrings. +/// +/// Returns `true` if any of the symbols found in the object file at +/// `path` contain a substring listed in `substrings`. +/// +/// Panics if `path` is not a valid object file readable by the current user. +pub fn any_symbol_contains(path: impl AsRef, substrings: &[&str]) -> bool { + with_symbol_iter(path, |syms| { + for sym in syms { + for substring in substrings { + if sym + .name_bytes() + .unwrap() + .windows(substring.len()) + .any(|x| x == substring.as_bytes()) + { + eprintln!("{:?} contains {}", sym, substring); + return true; + } + } + } + false + }) +} diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 87a1729d2b418..6d3e488bb0821 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -15,7 +15,7 @@ env: CARGO_NET_RETRY: 10 CI: 1 RUST_BACKTRACE: short - RUSTFLAGS: "-D warnings -W unreachable-pub -W bare-trait-objects" + RUSTFLAGS: "-D warnings -D elided_lifetimes_in_paths -D explicit_outlives_requirements -D unsafe_op_in_unsafe_fn -D unused_extern_crates -D unused_lifetimes -D unreachable_pub" RUSTUP_MAX_RETRIES: 10 jobs: diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index c2e8b265218fb..e7e764ce03517 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -14,6 +14,8 @@ extend-ignore-re = [ "\\w*\\.{3,4}\\w*", '"flate2"', "raison d'être", + "inout", + "optin" ] [default.extend-words] diff --git a/src/tools/rust-analyzer/.vscode/launch.json b/src/tools/rust-analyzer/.vscode/launch.json index c353737a35a83..e83c03796a4b5 100644 --- a/src/tools/rust-analyzer/.vscode/launch.json +++ b/src/tools/rust-analyzer/.vscode/launch.json @@ -18,7 +18,8 @@ "args": [ // "--user-data-dir=${workspaceFolder}/target/code", "--disable-extensions", - "--extensionDevelopmentPath=${workspaceFolder}/editors/code" + "--extensionDevelopmentPath=${workspaceFolder}/editors/code", + "--log rust-lang.rust-analyzer:debug" ], "outFiles": [ "${workspaceFolder}/editors/code/out/**/*.js" @@ -36,7 +37,8 @@ "runtimeExecutable": "${execPath}", "args": [ "--disable-extensions", - "--extensionDevelopmentPath=${workspaceFolder}/editors/code" + "--extensionDevelopmentPath=${workspaceFolder}/editors/code", + "--log rust-lang.rust-analyzer:debug" ], "outFiles": [ "${workspaceFolder}/editors/code/out/**/*.js" @@ -57,7 +59,8 @@ "runtimeExecutable": "${execPath}", "args": [ "--disable-extensions", - "--extensionDevelopmentPath=${workspaceFolder}/editors/code" + "--extensionDevelopmentPath=${workspaceFolder}/editors/code", + "--log rust-lang.rust-analyzer:debug" ], "outFiles": [ "${workspaceFolder}/editors/code/out/**/*.js" @@ -79,7 +82,8 @@ "runtimeExecutable": "${execPath}", "args": [ "--disable-extension", "rust-lang.rust-analyzer", - "--extensionDevelopmentPath=${workspaceFolder}/editors/code" + "--extensionDevelopmentPath=${workspaceFolder}/editors/code", + "--log rust-lang.rust-analyzer:debug" ], "outFiles": [ "${workspaceFolder}/editors/code/out/**/*.js" diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e9ebe26f42c2c..b98a1195d8bb7 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -52,16 +52,16 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.32.2", + "object 0.35.0", "rustc-demangle", ] @@ -70,6 +70,7 @@ name = "base-db" version = "0.0.0" dependencies = [ "cfg", + "intern", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lz4_flex", "rustc-hash", @@ -103,9 +104,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -135,9 +136,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg" @@ -146,6 +147,7 @@ dependencies = [ "arbitrary", "derive_arbitrary", "expect-test", + "intern", "mbe", "oorandom", "rustc-hash", @@ -230,18 +232,18 @@ checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -267,9 +269,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "ctrlc" @@ -364,9 +366,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "ena" @@ -429,6 +431,7 @@ dependencies = [ "crossbeam-channel", "paths", "process-wrap", + "project-model", "rustc-hash", "serde", "serde_json", @@ -474,9 +477,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "hashbrown" @@ -835,6 +838,7 @@ dependencies = [ "dashmap", "hashbrown", "rustc-hash", + "sptr", "triomphe", ] @@ -913,9 +917,9 @@ dependencies = [ [[package]] name = "libmimalloc-sys" -version = "0.1.37" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7" +checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6" dependencies = [ "cc", "libc", @@ -968,6 +972,7 @@ dependencies = [ "crossbeam-channel", "hir-expand", "ide-db", + "intern", "itertools", "paths", "proc-macro-api", @@ -1044,6 +1049,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "cov-mark", + "intern", "parser", "rustc-hash", "smallvec", @@ -1081,18 +1087,18 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d" +checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176" dependencies = [ "libmimalloc-sys", ] [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -1157,9 +1163,9 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.49.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" +checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" dependencies = [ "windows-sys 0.48.0", ] @@ -1182,18 +1188,18 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d" dependencies = [ "memchr", ] [[package]] name = "object" -version = "0.33.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] @@ -1218,9 +1224,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1323,6 +1329,7 @@ version = "0.0.0" dependencies = [ "base-db", "indexmap", + "intern", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "rustc-hash", @@ -1341,6 +1348,7 @@ version = "0.0.0" dependencies = [ "base-db", "expect-test", + "intern", "libloading", "mbe", "memmap2", @@ -1372,9 +1380,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -1411,6 +1419,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", + "intern", "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", @@ -1651,6 +1660,7 @@ dependencies = [ "ide", "ide-db", "ide-ssr", + "intern", "itertools", "load-cargo", "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1791,18 +1801,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -1834,9 +1844,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -1858,9 +1868,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol_str" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" dependencies = [ "serde", ] @@ -1885,6 +1895,12 @@ dependencies = [ "vfs", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1907,9 +1923,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1958,6 +1974,7 @@ dependencies = [ "base-db", "cfg", "hir-expand", + "intern", "rustc-hash", "span", "stdx", @@ -1993,18 +2010,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -2088,9 +2105,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -2100,18 +2117,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -2185,9 +2202,9 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" +checksum = "b56c62d2c80033cb36fae448730a2f2ef99410fe3ecbffc916681a32f6807dbe" dependencies = [ "nu-ansi-term", "tracing-core", @@ -2197,9 +2214,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90" dependencies = [ "serde", "stable_deref_trait", @@ -2210,7 +2227,8 @@ name = "tt" version = "0.0.0" dependencies = [ "arrayvec", - "smol_str", + "intern", + "ra-ap-rustc_lexer", "stdx", "text-size", ] @@ -2538,9 +2556,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" dependencies = [ "memchr", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d4c3b7a3bfbd2..c2f601a91bc7a 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -50,7 +50,7 @@ debug = 2 [workspace.dependencies] # local crates base-db = { path = "./crates/base-db", version = "0.0.0" } -cfg = { path = "./crates/cfg", version = "0.0.0" } +cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] } flycheck = { path = "./crates/flycheck", version = "0.0.0" } hir = { path = "./crates/hir", version = "0.0.0" } hir-def = { path = "./crates/hir-def", version = "0.0.0" } @@ -163,14 +163,14 @@ xshell = "0.2.5" dashmap = { version = "=5.5.3", features = ["raw-api"] } [workspace.lints.rust] -bare_trait_objects = "warn" +# remember to update RUSTFLAGS in ci.yml if you add something here + elided_lifetimes_in_paths = "warn" -ellipsis_inclusive_range_patterns = "warn" explicit_outlives_requirements = "warn" +unsafe_op_in_unsafe_fn = "warn" unused_extern_crates = "warn" unused_lifetimes = "warn" unreachable_pub = "warn" -semicolon_in_expressions_from_macros = "warn" [workspace.lints.clippy] # FIXME Remove the tidy test once the lint table is stable diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 4ab99fc33c466..1b1ee034cac2f 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -27,6 +27,7 @@ stdx.workspace = true syntax.workspace = true vfs.workspace = true span.workspace = true +intern.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 1d172ab9e40e3..460581f4a6c06 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -9,16 +9,14 @@ use std::{fmt, mem, ops}; use cfg::CfgOptions; +use intern::Symbol; use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxHashMap, FxHashSet}; -use span::Edition; -use syntax::SmolStr; +use span::{Edition, EditionedFileId}; use triomphe::Arc; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; -// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`, -// then the crate for the proc-macro hasn't been build yet as the build data is missing. -pub type ProcMacroPaths = FxHashMap, AbsPathBuf), String>>; +pub type ProcMacroPaths = FxHashMap>; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SourceRootId(pub u32); @@ -99,8 +97,8 @@ impl fmt::Debug for CrateGraph { pub type CrateId = Idx; -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct CrateName(SmolStr); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CrateName(Symbol); impl CrateName { /// Creates a crate name, checking for dashes in the string provided. @@ -110,16 +108,16 @@ impl CrateName { if name.contains('-') { Err(name) } else { - Ok(Self(SmolStr::new(name))) + Ok(Self(Symbol::intern(name))) } } /// Creates a crate name, unconditionally replacing the dashes with underscores. pub fn normalize_dashes(name: &str) -> CrateName { - Self(SmolStr::new(name.replace('-', "_"))) + Self(Symbol::intern(&name.replace('-', "_"))) } - pub fn as_smol_str(&self) -> &SmolStr { + pub fn symbol(&self) -> &Symbol { &self.0 } } @@ -133,7 +131,7 @@ impl fmt::Display for CrateName { impl ops::Deref for CrateName { type Target = str; fn deref(&self) -> &str { - &self.0 + self.0.as_str() } } @@ -141,11 +139,11 @@ impl ops::Deref for CrateName { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CrateOrigin { /// Crates that are from the rustc workspace. - Rustc { name: String }, + Rustc { name: Symbol }, /// Crates that are workspace members. - Local { repo: Option, name: Option }, + Local { repo: Option, name: Option }, /// Crates that are non member libraries. - Library { repo: Option, name: String }, + Library { repo: Option, name: Symbol }, /// Crates that are provided by the language, like std, core, proc-macro, ... Lang(LangCrateOrigin), } @@ -201,16 +199,16 @@ impl fmt::Display for LangCrateOrigin { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateDisplayName { // The name we use to display various paths (with `_`). crate_name: CrateName, // The name as specified in Cargo.toml (with `-`). - canonical_name: String, + canonical_name: Symbol, } impl CrateDisplayName { - pub fn canonical_name(&self) -> &str { + pub fn canonical_name(&self) -> &Symbol { &self.canonical_name } pub fn crate_name(&self) -> &CrateName { @@ -220,7 +218,7 @@ impl CrateDisplayName { impl From for CrateDisplayName { fn from(crate_name: CrateName) -> CrateDisplayName { - let canonical_name = crate_name.to_string(); + let canonical_name = crate_name.0.clone(); CrateDisplayName { crate_name, canonical_name } } } @@ -239,9 +237,9 @@ impl ops::Deref for CrateDisplayName { } impl CrateDisplayName { - pub fn from_canonical_name(canonical_name: String) -> CrateDisplayName { - let crate_name = CrateName::normalize_dashes(&canonical_name); - CrateDisplayName { crate_name, canonical_name } + pub fn from_canonical_name(canonical_name: &str) -> CrateDisplayName { + let crate_name = CrateName::normalize_dashes(canonical_name); + CrateDisplayName { crate_name, canonical_name: Symbol::intern(canonical_name) } } } @@ -662,6 +660,10 @@ impl CrateData { fn add_dep(&mut self, dep: Dependency) { self.dependencies.push(dep) } + + pub fn root_file_id(&self) -> EditionedFileId { + EditionedFileId::new(self.root_file_id, self.edition) + } } impl Extend<(String, String)> for Env { 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 96fbbc317d487..f319f98537b60 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -6,8 +6,10 @@ mod input; use std::panic; use salsa::Durability; +use span::EditionedFileId; use syntax::{ast, Parse, SourceFile, SyntaxError}; use triomphe::Arc; +use vfs::FileId; pub use crate::{ change::FileChange, @@ -18,8 +20,7 @@ pub use crate::{ }, }; pub use salsa::{self, Cancelled}; -pub use span::{FilePosition, FileRange}; -pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath}; +pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, VfsPath}; pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; @@ -41,9 +42,9 @@ 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 = 2024; +pub const DEFAULT_FILE_TEXT_LRU_CAP: u16 = 16; +pub const DEFAULT_PARSE_LRU_CAP: u16 = 128; +pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024; pub trait FileLoader { /// Text of the file. @@ -58,10 +59,11 @@ pub trait FileLoader { #[salsa::query_group(SourceDatabaseStorage)] pub trait SourceDatabase: FileLoader + std::fmt::Debug { /// Parses the file into the syntax tree. - fn parse(&self, file_id: FileId) -> Parse; + #[salsa::lru] + fn parse(&self, file_id: EditionedFileId) -> Parse; /// Returns the set of errors obtained from parsing the file including validation errors. - fn parse_errors(&self, file_id: FileId) -> Option>; + fn parse_errors(&self, file_id: EditionedFileId) -> Option>; /// The crate graph. #[salsa::input] @@ -82,14 +84,14 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option Parse { +fn parse(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Parse { let _p = tracing::info_span!("parse", ?file_id).entered(); + let (file_id, edition) = file_id.unpack(); let text = db.file_text(file_id); - // FIXME: Edition based parsing - SourceFile::parse(&text, span::Edition::CURRENT) + SourceFile::parse(&text, edition) } -fn parse_errors(db: &dyn SourceDatabase, file_id: FileId) -> Option> { +fn parse_errors(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Option> { let errors = db.parse(file_id).errors(); match &*errors { [] => None, @@ -104,6 +106,7 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>; + #[salsa::lru] fn file_text(&self, file_id: FileId) -> Arc; /// Path to a file, relative to the root of its source root. diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml index 9b3a5026ac800..faf93f62c6afb 100644 --- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml +++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml @@ -15,7 +15,8 @@ doctest = false rustc-hash.workspace = true # locals deps -tt.workspace = true +tt = { workspace = true, optional = true } +intern.workspace = true [dev-dependencies] expect-test = "1.4.1" 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 b7dbb7b5fdd79..35c0c89c70cfa 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -2,20 +2,20 @@ //! //! See: -use std::{fmt, slice::Iter as SliceIter}; +use std::fmt; -use tt::SmolStr; +use intern::Symbol; /// A simple configuration value passed in from the outside. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CfgAtom { /// eg. `#[cfg(test)]` - Flag(SmolStr), + Flag(Symbol), /// eg. `#[cfg(target_os = "linux")]` /// /// Note that a key can have multiple values that are all considered "active" at the same time. /// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`. - KeyValue { key: SmolStr, value: SmolStr }, + KeyValue { key: Symbol, value: Symbol }, } impl fmt::Display for CfgAtom { @@ -32,8 +32,8 @@ impl fmt::Display for CfgAtom { pub enum CfgExpr { Invalid, Atom(CfgAtom), - All(Vec), - Any(Vec), + All(Box<[CfgExpr]>), + Any(Box<[CfgExpr]>), Not(Box), } @@ -44,6 +44,7 @@ impl From for CfgExpr { } impl CfgExpr { + #[cfg(feature = "tt")] pub fn parse(tt: &tt::Subtree) -> CfgExpr { next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) } @@ -63,10 +64,14 @@ impl CfgExpr { } } } -fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { + +#[cfg(feature = "tt")] +fn next_cfg_expr(it: &mut std::slice::Iter<'_, tt::TokenTree>) -> Option { + use intern::sym; + let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(), + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(CfgExpr::Invalid), }; @@ -77,10 +82,7 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { it.next(); it.next(); - // FIXME: escape? raw string? - let value = - SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); - CfgAtom::KeyValue { key: name, value }.into() + CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into() } _ => return Some(CfgExpr::Invalid), } @@ -88,11 +90,13 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(tt::TokenTree::Subtree(subtree)) => { it.next(); let mut sub_it = subtree.token_trees.iter(); - let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect(); - match name.as_str() { - "all" => CfgExpr::All(subs), - "any" => CfgExpr::Any(subs), - "not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))), + let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)); + match name { + s if s == sym::all => CfgExpr::All(subs.collect()), + s if s == sym::any => CfgExpr::Any(subs.collect()), + s if s == sym::not => { + CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid))) + } _ => CfgExpr::Invalid, } } @@ -112,11 +116,11 @@ fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option impl arbitrary::Arbitrary<'_> for CfgAtom { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { if u.arbitrary()? { - Ok(CfgAtom::Flag(String::arbitrary(u)?.into())) + Ok(CfgAtom::Flag(Symbol::intern(<_>::arbitrary(u)?))) } else { Ok(CfgAtom::KeyValue { - key: String::arbitrary(u)?.into(), - value: String::arbitrary(u)?.into(), + key: Symbol::intern(<_>::arbitrary(u)?), + value: Symbol::intern(<_>::arbitrary(u)?), }) } } diff --git a/src/tools/rust-analyzer/crates/cfg/src/dnf.rs b/src/tools/rust-analyzer/crates/cfg/src/dnf.rs index fd80e1ebe683b..f3ebca0465098 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/dnf.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/dnf.rs @@ -27,7 +27,7 @@ struct Literal { } impl DnfExpr { - pub fn new(expr: CfgExpr) -> Self { + pub fn new(expr: &CfgExpr) -> Self { let builder = Builder { expr: DnfExpr { conjunctions: Vec::new() } }; builder.lower(expr) @@ -66,9 +66,9 @@ impl DnfExpr { } } - res.enabled.sort_unstable(); + res.enabled.sort_unstable_by(compare); res.enabled.dedup(); - res.disabled.sort_unstable(); + res.disabled.sort_unstable_by(compare); res.disabled.dedup(); Some(res) } @@ -114,14 +114,25 @@ impl DnfExpr { }; // Undo the FxHashMap randomization for consistent output. - diff.enable.sort_unstable(); - diff.disable.sort_unstable(); + diff.enable.sort_unstable_by(compare); + diff.disable.sort_unstable_by(compare); Some(diff) }) } } +fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering { + match (a, b) { + (CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()), + (CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less, + (CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater, + (CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => { + key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str())) + } + } +} + impl fmt::Display for DnfExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.conjunctions.len() != 1 { @@ -143,9 +154,9 @@ impl fmt::Display for DnfExpr { } impl Conjunction { - fn new(parts: Vec) -> Self { + fn new(parts: Box<[CfgExpr]>) -> Self { let mut literals = Vec::new(); - for part in parts { + for part in parts.into_vec() { match part { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => { literals.push(Literal::new(part)); @@ -221,27 +232,28 @@ struct Builder { } impl Builder { - fn lower(mut self, expr: CfgExpr) -> DnfExpr { + fn lower(mut self, expr: &CfgExpr) -> DnfExpr { let expr = make_nnf(expr); let expr = make_dnf(expr); match expr { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => { - self.expr.conjunctions.push(Conjunction::new(vec![expr])); + self.expr.conjunctions.push(Conjunction::new(Box::new([expr]))); } CfgExpr::All(conj) => { self.expr.conjunctions.push(Conjunction::new(conj)); } - CfgExpr::Any(mut disj) => { + CfgExpr::Any(disj) => { + let mut disj = disj.into_vec(); disj.reverse(); while let Some(conj) = disj.pop() { match conj { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::All(_) | CfgExpr::Not(_) => { - self.expr.conjunctions.push(Conjunction::new(vec![conj])); + self.expr.conjunctions.push(Conjunction::new(Box::new([conj]))); } CfgExpr::Any(inner_disj) => { // Flatten. - disj.extend(inner_disj.into_iter().rev()); + disj.extend(inner_disj.into_vec().into_iter().rev()); } } } @@ -255,11 +267,11 @@ impl Builder { fn make_dnf(expr: CfgExpr) -> CfgExpr { match expr { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr, - CfgExpr::Any(e) => flatten(CfgExpr::Any(e.into_iter().map(make_dnf).collect())), + CfgExpr::Any(e) => flatten(CfgExpr::Any(e.into_vec().into_iter().map(make_dnf).collect())), CfgExpr::All(e) => { - let e = e.into_iter().map(make_dnf).collect::>(); + let e = e.into_vec().into_iter().map(make_dnf).collect::>(); - flatten(CfgExpr::Any(distribute_conj(&e))) + flatten(CfgExpr::Any(distribute_conj(&e).into_boxed_slice())) } } } @@ -270,7 +282,7 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec { match rest { [head, tail @ ..] => match head { CfgExpr::Any(disj) => { - for part in disj { + for part in disj.iter() { with.push(part.clone()); go(out, with, tail); with.pop(); @@ -284,7 +296,7 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec { }, _ => { // Turn accumulated parts into a new conjunction. - out.push(CfgExpr::All(with.clone())); + out.push(CfgExpr::All(with.clone().into_boxed_slice())); } } } @@ -297,25 +309,27 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec { out } -fn make_nnf(expr: CfgExpr) -> CfgExpr { +fn make_nnf(expr: &CfgExpr) -> CfgExpr { match expr { - CfgExpr::Invalid | CfgExpr::Atom(_) => expr, - CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(make_nnf).collect()), - CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(make_nnf).collect()), - CfgExpr::Not(operand) => match *operand { - CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr - CfgExpr::Not(expr) => { - // Remove double negation. - make_nnf(*expr) - } - // Convert negated conjunction/disjunction using DeMorgan's Law. - CfgExpr::Any(inner) => CfgExpr::All( - inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(), - ), - CfgExpr::All(inner) => CfgExpr::Any( - inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(), - ), - }, + CfgExpr::Invalid | CfgExpr::Atom(_) => expr.clone(), + CfgExpr::Any(expr) => CfgExpr::Any(expr.iter().map(make_nnf).collect()), + CfgExpr::All(expr) => CfgExpr::All(expr.iter().map(make_nnf).collect()), + CfgExpr::Not(operand) => make_nnf_neg(operand), + } +} + +fn make_nnf_neg(operand: &CfgExpr) -> CfgExpr { + match operand { + // Original negated expr + CfgExpr::Invalid => CfgExpr::Not(Box::new(CfgExpr::Invalid)), // Original negated expr + // Original negated expr + CfgExpr::Atom(atom) => CfgExpr::Not(Box::new(CfgExpr::Atom(atom.clone()))), + // Remove double negation. + CfgExpr::Not(expr) => make_nnf(expr), + // Convert negated conjunction/disjunction using DeMorgan's Law. + CfgExpr::Any(inner) => CfgExpr::All(inner.iter().map(make_nnf_neg).collect()), + // Convert negated conjunction/disjunction using DeMorgan's Law. + CfgExpr::All(inner) => CfgExpr::Any(inner.iter().map(make_nnf_neg).collect()), } } @@ -324,20 +338,22 @@ fn flatten(expr: CfgExpr) -> CfgExpr { match expr { CfgExpr::All(inner) => CfgExpr::All( inner - .into_iter() + .iter() .flat_map(|e| match e { - CfgExpr::All(inner) => inner, - _ => vec![e], + CfgExpr::All(inner) => inner.as_ref(), + _ => std::slice::from_ref(e), }) + .cloned() .collect(), ), CfgExpr::Any(inner) => CfgExpr::Any( inner - .into_iter() + .iter() .flat_map(|e| match e { - CfgExpr::Any(inner) => inner, - _ => vec![e], + CfgExpr::Any(inner) => inner.as_ref(), + _ => std::slice::from_ref(e), }) + .cloned() .collect(), ), _ => expr, diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 8b30286a0a8a8..6d46dfb99949b 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -8,7 +8,8 @@ mod tests; use std::fmt; use rustc_hash::FxHashSet; -use tt::SmolStr; + +use intern::Symbol; pub use cfg_expr::{CfgAtom, CfgExpr}; pub use dnf::DnfExpr; @@ -48,11 +49,11 @@ impl CfgOptions { cfg.fold(&|atom| self.enabled.contains(atom)) } - pub fn insert_atom(&mut self, key: SmolStr) { + pub fn insert_atom(&mut self, key: Symbol) { self.enabled.insert(CfgAtom::Flag(key)); } - pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { + pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) { self.enabled.insert(CfgAtom::KeyValue { key, value }); } @@ -66,19 +67,16 @@ impl CfgOptions { } } - pub fn get_cfg_keys(&self) -> impl Iterator { + pub fn get_cfg_keys(&self) -> impl Iterator { self.enabled.iter().map(|it| match it { CfgAtom::Flag(key) => key, CfgAtom::KeyValue { key, .. } => key, }) } - pub fn get_cfg_values<'a>( - &'a self, - cfg_key: &'a str, - ) -> impl Iterator + 'a { + pub fn get_cfg_values<'a>(&'a self, cfg_key: &'a str) -> impl Iterator + 'a { self.enabled.iter().filter_map(move |it| match it { - CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value), + CfgAtom::KeyValue { key, value } if cfg_key == key.as_str() => Some(value), _ => None, }) } diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index dddaf2cce1823..597023a792bb7 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -1,5 +1,6 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; +use intern::Symbol; use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ast, AstNode, Edition}; @@ -28,7 +29,7 @@ fn check_dnf(input: &str, expect: Expect) { DocCommentDesugarMode::ProcMacro, ); let cfg = CfgExpr::parse(&tt); - let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); + let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); expect.assert_eq(&actual); } @@ -42,7 +43,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { DocCommentDesugarMode::ProcMacro, ); let cfg = CfgExpr::parse(&tt); - let dnf = DnfExpr::new(cfg); + let dnf = DnfExpr::new(&cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); expect.assert_eq(&why_inactive); } @@ -58,40 +59,51 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { DocCommentDesugarMode::ProcMacro, ); let cfg = CfgExpr::parse(&tt); - let dnf = DnfExpr::new(cfg); + let dnf = DnfExpr::new(&cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::>(); assert_eq!(hints, expected_hints); } #[test] fn test_cfg_expr_parser() { - assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into()); - assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into()); + assert_parse_result("#![cfg(foo)]", CfgAtom::Flag(Symbol::intern("foo")).into()); + assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag(Symbol::intern("foo")).into()); assert_parse_result( "#![cfg(not(foo))]", - CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())), + CfgExpr::Not(Box::new(CfgAtom::Flag(Symbol::intern("foo")).into())), ); assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); // Only take the first - assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into()); + assert_parse_result( + r#"#![cfg(foo, bar = "baz")]"#, + CfgAtom::Flag(Symbol::intern("foo")).into(), + ); assert_parse_result( r#"#![cfg(all(foo, bar = "baz"))]"#, - CfgExpr::All(vec![ - CfgAtom::Flag("foo".into()).into(), - CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), - ]), + CfgExpr::All( + vec![ + CfgAtom::Flag(Symbol::intern("foo")).into(), + CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") } + .into(), + ] + .into_boxed_slice(), + ), ); assert_parse_result( r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, - CfgExpr::Any(vec![ - CfgExpr::Not(Box::new(CfgExpr::Invalid)), - CfgExpr::All(vec![]), - CfgExpr::Invalid, - CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), - ]), + CfgExpr::Any( + vec![ + CfgExpr::Not(Box::new(CfgExpr::Invalid)), + CfgExpr::All(Box::new([])), + CfgExpr::Invalid, + CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") } + .into(), + ] + .into_boxed_slice(), + ), ); } @@ -167,7 +179,7 @@ fn hints() { check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]); - opts.insert_atom("test".into()); + opts.insert_atom(Symbol::intern("test")); check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); @@ -180,7 +192,7 @@ fn hints_impossible() { check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); - opts.insert_atom("test".into()); + opts.insert_atom(Symbol::intern("test")); check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); } @@ -188,8 +200,8 @@ fn hints_impossible() { #[test] fn why_inactive() { let mut opts = CfgOptions::default(); - opts.insert_atom("test".into()); - opts.insert_atom("test2".into()); + opts.insert_atom(Symbol::intern("test")); + opts.insert_atom(Symbol::intern("test2")); check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]); check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]); @@ -231,6 +243,6 @@ fn proptest() { let mut u = Unstructured::new(&buf); let cfg = CfgExpr::arbitrary(&mut u).unwrap(); - DnfExpr::new(cfg); + DnfExpr::new(&cfg); } } diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index d81a5fe340066..bb3a94c8da678 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -24,6 +24,7 @@ process-wrap.workspace = true paths.workspace = true stdx.workspace = true toolchain.workspace = true +project-model.workspace = true [lints] workspace = true \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index 778def5d2bed6..3dd2a91d8fd96 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -20,10 +20,11 @@ pub use cargo_metadata::diagnostic::{ use toolchain::Tool; mod command; +pub mod project_json; mod test_runner; use command::{CommandHandle, ParseFromLine}; -pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState}; +pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget}; #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum InvocationStrategy { @@ -240,7 +241,7 @@ enum FlycheckStatus { Finished, } -const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; +pub const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; impl FlycheckActor { fn new( diff --git a/src/tools/rust-analyzer/crates/flycheck/src/project_json.rs b/src/tools/rust-analyzer/crates/flycheck/src/project_json.rs new file mode 100644 index 0000000000000..b6e4495bc6d6e --- /dev/null +++ b/src/tools/rust-analyzer/crates/flycheck/src/project_json.rs @@ -0,0 +1,152 @@ +//! A `cargo-metadata`-equivalent for non-Cargo build systems. +use std::{io, process::Command}; + +use crossbeam_channel::Sender; +use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf}; +use project_model::ProjectJsonData; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::command::{CommandHandle, ParseFromLine}; + +pub const ARG_PLACEHOLDER: &str = "{arg}"; + +/// A command wrapper for getting a `rust-project.json`. +/// +/// This is analogous to `cargo-metadata`, but for non-Cargo build systems. +pub struct Discover { + command: Vec, + sender: Sender, +} + +#[derive(PartialEq, Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum DiscoverArgument { + Path(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf), + Buildfile(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf), +} + +fn serialize_abs_pathbuf(path: &AbsPathBuf, se: S) -> Result +where + S: serde::Serializer, +{ + let path: &Utf8Path = path.as_ref(); + se.serialize_str(path.as_str()) +} + +impl Discover { + /// Create a new [Discover]. + pub fn new(sender: Sender, command: Vec) -> Self { + Self { sender, command } + } + + /// Spawn the command inside [Discover] and report progress, if any. + pub fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result { + let command = &self.command[0]; + let args = &self.command[1..]; + + let args: Vec = args + .iter() + .map(|arg| { + if arg == ARG_PLACEHOLDER { + serde_json::to_string(&discover_arg).expect("Unable to serialize args") + } else { + arg.to_owned() + } + }) + .collect(); + + let mut cmd = Command::new(command); + cmd.args(args); + + Ok(DiscoverHandle { _handle: CommandHandle::spawn(cmd, self.sender.clone())? }) + } +} + +/// A handle to a spawned [Discover]. +#[derive(Debug)] +pub struct DiscoverHandle { + _handle: CommandHandle, +} + +/// An enum containing either progress messages, an error, +/// or the materialized `rust-project`. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "kind")] +#[serde(rename_all = "snake_case")] +enum DiscoverProjectData { + Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, + Error { error: String, source: Option }, + Progress { message: String }, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum DiscoverProjectMessage { + Finished { project: ProjectJsonData, buildfile: AbsPathBuf }, + Error { error: String, source: Option }, + Progress { message: String }, +} + +impl DiscoverProjectMessage { + fn new(data: DiscoverProjectData) -> Self { + match data { + DiscoverProjectData::Finished { project, buildfile, .. } => { + let buildfile = buildfile.try_into().expect("Unable to make path absolute"); + DiscoverProjectMessage::Finished { project, buildfile } + } + DiscoverProjectData::Error { error, source } => { + DiscoverProjectMessage::Error { error, source } + } + DiscoverProjectData::Progress { message } => { + DiscoverProjectMessage::Progress { message } + } + } + } +} + +impl ParseFromLine for DiscoverProjectMessage { + fn from_line(line: &str, _error: &mut String) -> Option { + // can the line even be deserialized as JSON? + let Ok(data) = serde_json::from_str::(line) else { + let err = DiscoverProjectData::Error { error: line.to_owned(), source: None }; + return Some(DiscoverProjectMessage::new(err)); + }; + + let Ok(data) = serde_json::from_value::(data) else { + return None; + }; + + let msg = DiscoverProjectMessage::new(data); + Some(msg) + } + + fn from_eof() -> Option { + None + } +} + +#[test] +fn test_deserialization() { + let message = r#" + {"kind": "progress", "message":"querying build system","input":{"files":["src/main.rs"]}} + "#; + let message: DiscoverProjectData = + serde_json::from_str(message).expect("Unable to deserialize message"); + assert!(matches!(message, DiscoverProjectData::Progress { .. })); + + let message = r#" + {"kind": "error", "error":"failed to deserialize command output","source":"command"} + "#; + + let message: DiscoverProjectData = + serde_json::from_str(message).expect("Unable to deserialize message"); + assert!(matches!(message, DiscoverProjectData::Error { .. })); + + let message = r#" + {"kind": "finished", "project": {"sysroot": "foo", "crates": [], "runnables": []}, "buildfile":"rust-analyzer/BUILD"} + "#; + + let message: DiscoverProjectData = + serde_json::from_str(message).expect("Unable to deserialize message"); + assert!(matches!(message, DiscoverProjectData::Finished { .. })); +} 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 c136dd13664a7..74ebca34103aa 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs @@ -59,19 +59,38 @@ pub struct CargoTestHandle { } // Example of a cargo test command: -// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json +// cargo test --workspace --no-fail-fast -- -Z unstable-options --format=json +// or +// cargo test --package my-package --no-fail-fast -- module::func -Z unstable-options --format=json + +#[derive(Debug)] +pub enum TestTarget { + Workspace, + Package(String), +} impl CargoTestHandle { pub fn new( path: Option<&str>, options: CargoOptions, root: &AbsPath, + test_target: TestTarget, sender: Sender, ) -> std::io::Result { let mut cmd = Command::new(Tool::Cargo.path()); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); - cmd.arg("--workspace"); + + match &test_target { + TestTarget::Package(package) => { + cmd.arg("--package"); + cmd.arg(package); + } + TestTarget::Workspace => { + cmd.arg("--workspace"); + } + }; + // --no-fail-fast is needed to ensure that all requested tests will run cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 184dab8367c16..ba88495e14d30 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -1,6 +1,6 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. -use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter}; +use std::{borrow::Cow, hash::Hash, ops, slice}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -9,17 +9,18 @@ use hir_expand::{ attrs::{collect_attrs, Attr, AttrId, RawAttrs}, HirFileId, InFile, }; +use intern::{sym, Symbol}; use la_arena::{ArenaMap, Idx, RawIdx}; use mbe::DelimiterKind; use syntax::{ ast::{self, HasAttrs}, - AstPtr, SmolStr, + AstPtr, }; use triomphe::Arc; use crate::{ db::DefDatabase, - item_tree::{AttrOwner, Fields, ItemTreeNode}, + item_tree::{AttrOwner, FieldParent, ItemTreeNode}, lang_item::LangItem, nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource}, @@ -75,40 +76,36 @@ impl Attrs { let mut res = ArenaMap::default(); let crate_graph = db.crate_graph(); - let (fields, item_tree, krate) = match v { + let item_tree; + let (parent, fields, krate) = match v { VariantId::EnumVariantId(it) => { let loc = it.lookup(db); let krate = loc.parent.lookup(db).container.krate; - let item_tree = loc.id.item_tree(db); + item_tree = loc.id.item_tree(db); let variant = &item_tree[loc.id.value]; - (variant.fields.clone(), item_tree, krate) + (FieldParent::Variant(loc.id.value), &variant.fields, krate) } VariantId::StructId(it) => { let loc = it.lookup(db); let krate = loc.container.krate; - let item_tree = loc.id.item_tree(db); + item_tree = loc.id.item_tree(db); let struct_ = &item_tree[loc.id.value]; - (struct_.fields.clone(), item_tree, krate) + (FieldParent::Struct(loc.id.value), &struct_.fields, krate) } VariantId::UnionId(it) => { let loc = it.lookup(db); let krate = loc.container.krate; - let item_tree = loc.id.item_tree(db); + item_tree = loc.id.item_tree(db); let union_ = &item_tree[loc.id.value]; - (union_.fields.clone(), item_tree, krate) + (FieldParent::Union(loc.id.value), &union_.fields, krate) } }; - let fields = match fields { - Fields::Record(fields) | Fields::Tuple(fields) => fields, - Fields::Unit => return Arc::new(res), - }; - let cfg_options = &crate_graph[krate].cfg_options; let mut idx = 0; - for field in fields { - let attrs = item_tree.attrs(db, krate, field.into()); + for (id, _field) in fields.iter().enumerate() { + let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, id)); if attrs.is_cfg_enabled(cfg_options) { res.insert(Idx::from_raw(RawIdx::from(idx)), attrs); idx += 1; @@ -120,12 +117,12 @@ impl Attrs { } impl Attrs { - pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { + pub fn by_key<'attrs>(&'attrs self, key: &'attrs Symbol) -> AttrQuery<'_> { AttrQuery { attrs: self, key } } pub fn cfg(&self) -> Option { - let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse); + let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse); let first = cfgs.next()?; match cfgs.next() { Some(second) => { @@ -137,7 +134,7 @@ impl Attrs { } pub fn cfgs(&self) -> impl Iterator + '_ { - self.by_key("cfg").tt_values().map(CfgExpr::parse) + self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse) } pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { @@ -147,50 +144,50 @@ impl Attrs { } } - pub fn lang(&self) -> Option<&str> { - self.by_key("lang").string_value() + pub fn lang(&self) -> Option<&Symbol> { + self.by_key(&sym::lang).string_value() } pub fn lang_item(&self) -> Option { - self.by_key("lang").string_value().and_then(LangItem::from_str) + self.by_key(&sym::lang).string_value().and_then(LangItem::from_symbol) } pub fn has_doc_hidden(&self) -> bool { - self.by_key("doc").tt_values().any(|tt| { + self.by_key(&sym::doc).tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "hidden") + matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) }) } pub fn has_doc_notable_trait(&self) -> bool { - self.by_key("doc").tt_values().any(|tt| { + self.by_key(&sym::doc).tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "notable_trait") + matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) }) } pub fn doc_exprs(&self) -> impl Iterator + '_ { - self.by_key("doc").tt_values().map(DocExpr::parse) + self.by_key(&sym::doc).tt_values().map(DocExpr::parse) } - pub fn doc_aliases(&self) -> impl Iterator + '_ { + pub fn doc_aliases(&self) -> impl Iterator + '_ { self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) } - pub fn export_name(&self) -> Option<&str> { - self.by_key("export_name").string_value() + pub fn export_name(&self) -> Option<&Symbol> { + self.by_key(&sym::export_name).string_value() } pub fn is_proc_macro(&self) -> bool { - self.by_key("proc_macro").exists() + self.by_key(&sym::proc_macro).exists() } pub fn is_proc_macro_attribute(&self) -> bool { - self.by_key("proc_macro_attribute").exists() + self.by_key(&sym::proc_macro_attribute).exists() } pub fn is_proc_macro_derive(&self) -> bool { - self.by_key("proc_macro_derive").exists() + self.by_key(&sym::proc_macro_derive).exists() } pub fn is_test(&self) -> bool { @@ -199,33 +196,37 @@ impl Attrs { .segments() .iter() .rev() - .zip(["core", "prelude", "v1", "test"].iter().rev()) - .all(|it| it.0.as_str() == Some(it.1)) + .zip( + [sym::core.clone(), sym::prelude.clone(), sym::v1.clone(), sym::test.clone()] + .iter() + .rev(), + ) + .all(|it| it.0 == it.1) }) } pub fn is_ignore(&self) -> bool { - self.by_key("ignore").exists() + self.by_key(&sym::ignore).exists() } pub fn is_bench(&self) -> bool { - self.by_key("bench").exists() + self.by_key(&sym::bench).exists() } pub fn is_unstable(&self) -> bool { - self.by_key("unstable").exists() + self.by_key(&sym::unstable).exists() } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum DocAtom { /// eg. `#[doc(hidden)]` - Flag(SmolStr), + Flag(Symbol), /// eg. `#[doc(alias = "it")]` /// /// Note that a key can have multiple values that are all considered "active" at the same time. /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. - KeyValue { key: SmolStr, value: SmolStr }, + KeyValue { key: Symbol, value: Symbol }, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -234,7 +235,7 @@ pub enum DocExpr { /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]` Atom(DocAtom), /// eg. `#[doc(alias("x", "y"))]` - Alias(Vec), + Alias(Vec), } impl From for DocExpr { @@ -248,9 +249,9 @@ impl DocExpr { next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid) } - pub fn aliases(&self) -> &[SmolStr] { + pub fn aliases(&self) -> &[Symbol] { match self { - DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => { + DocExpr::Atom(DocAtom::KeyValue { key, value }) if *key == sym::alias => { std::slice::from_ref(value) } DocExpr::Alias(aliases) => aliases, @@ -259,10 +260,10 @@ impl DocExpr { } } -fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { +fn next_doc_expr(it: &mut slice::Iter<'_, tt::TokenTree>) -> Option { let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(), + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(DocExpr::Invalid), }; @@ -270,13 +271,14 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option let ret = match it.as_slice().first() { Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { match it.as_slice().get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + kind: tt::LitKind::Str, + .. + }))) => { it.next(); it.next(); - // FIXME: escape? raw string? - let value = - SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); - DocAtom::KeyValue { key: name, value }.into() + DocAtom::KeyValue { key: name, value: text.clone() }.into() } _ => return Some(DocExpr::Invalid), } @@ -284,8 +286,8 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(tt::TokenTree::Subtree(subtree)) => { it.next(); let subs = parse_comma_sep(subtree); - match name.as_str() { - "alias" => DocExpr::Alias(subs), + match &name { + s if *s == sym::alias => DocExpr::Alias(subs), _ => DocExpr::Invalid, } } @@ -301,15 +303,16 @@ fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option Some(ret) } -fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { +fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { subtree .token_trees .iter() .filter_map(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - // FIXME: escape? raw string? - Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"'))) - } + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + kind: tt::LitKind::Str, + symbol, + .. + })) => Some(symbol.clone()), _ => None, }) .collect() @@ -556,7 +559,7 @@ impl AttrSourceMap { #[derive(Debug, Clone, Copy)] pub struct AttrQuery<'attr> { attrs: &'attr Attrs, - key: &'static str, + key: &'attr Symbol, } impl<'attr> AttrQuery<'attr> { @@ -564,10 +567,14 @@ impl<'attr> AttrQuery<'attr> { self.attrs().filter_map(|attr| attr.token_tree_value()) } - pub fn string_value(self) -> Option<&'attr str> { + pub fn string_value(self) -> Option<&'attr Symbol> { self.attrs().find_map(|attr| attr.string_value()) } + pub fn string_value_with_span(self) -> Option<(&'attr Symbol, span::Span)> { + self.attrs().find_map(|attr| attr.string_value_with_span()) + } + pub fn string_value_unescape(self) -> Option> { self.attrs().find_map(|attr| attr.string_value_unescape()) } @@ -578,9 +585,7 @@ impl<'attr> AttrQuery<'attr> { pub fn attrs(self) -> impl Iterator + Clone { let key = self.key; - self.attrs - .iter() - .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_smol_str() == key)) + self.attrs.iter().filter(move |attr| attr.path.as_ident().map_or(false, |s| *s == *key)) } /// Find string value for a specific key inside token tree @@ -589,14 +594,14 @@ impl<'attr> AttrQuery<'attr> { /// #[doc(html_root_url = "url")] /// ^^^^^^^^^^^^^ key /// ``` - pub fn find_string_value_in_tt(self, key: &'attr str) -> Option<&SmolStr> { + pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&str> { self.tt_values().find_map(|tt| { let name = tt.token_trees.iter() - .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, ..} )) if text == key)) + .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), _ => None } }) @@ -647,11 +652,13 @@ mod tests { //! This module contains tests for doc-expression parsing. //! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`. + use intern::Symbol; + use span::EditionedFileId; use triomphe::Arc; - use base_db::FileId; use hir_expand::span_map::{RealSpanMap, SpanMap}; use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode}; + use span::FileId; use syntax::{ast, AstNode, TextRange}; use crate::attr::{DocAtom, DocExpr}; @@ -659,7 +666,9 @@ mod tests { fn assert_parse_result(input: &str, expected: DocExpr) { let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); + let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute( + EditionedFileId::current_edition(FileId::from_raw(0)), + ))); let tt = syntax_node_to_token_tree( tt.syntax(), map.as_ref(), @@ -672,24 +681,29 @@ mod tests { #[test] fn test_doc_expr_parser() { - assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into()); + assert_parse_result("#![doc(hidden)]", DocAtom::Flag(Symbol::intern("hidden")).into()); assert_parse_result( r#"#![doc(alias = "foo")]"#, - DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(), + DocAtom::KeyValue { key: Symbol::intern("alias"), value: Symbol::intern("foo") }.into(), ); - assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into())); + assert_parse_result( + r#"#![doc(alias("foo"))]"#, + DocExpr::Alias([Symbol::intern("foo")].into()), + ); assert_parse_result( r#"#![doc(alias("foo", "bar", "baz"))]"#, - DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()), + DocExpr::Alias( + [Symbol::intern("foo"), Symbol::intern("bar"), Symbol::intern("baz")].into(), + ), ); assert_parse_result( r#" #[doc(alias("Bar", "Qux"))] struct Foo;"#, - DocExpr::Alias(["Bar".into(), "Qux".into()].into()), + DocExpr::Alias([Symbol::intern("Bar"), Symbol::intern("Qux")].into()), ); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index ca4a3f5217cf3..d3c134f326604 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -6,13 +6,14 @@ pub mod scope; #[cfg(test)] mod tests; -use std::ops::Index; +use std::ops::{Deref, Index}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use hir_expand::{name::Name, InFile}; -use la_arena::{Arena, ArenaMap}; +use hir_expand::{name::Name, ExpandError, InFile}; +use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use rustc_hash::FxHashMap; +use smallvec::SmallVec; use span::MacroFileId; use syntax::{ast, AstPtr, SyntaxNodePtr}; use triomphe::Arc; @@ -23,6 +24,7 @@ use crate::{ hir::{ dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat, }, + item_tree::AttrOwner, nameres::DefMap, path::{ModPath, Path}, src::HasSource, @@ -91,6 +93,7 @@ pub struct BodySourceMap { label_map_back: ArenaMap, self_param: Option>>, + binding_definitions: FxHashMap>, /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). /// Instead, we use id of expression (`92`) to identify the field. @@ -112,8 +115,7 @@ pub struct SyntheticSyntax; #[derive(Debug, Eq, PartialEq)] pub enum BodyDiagnostic { InactiveCode { node: InFile, cfg: CfgExpr, opts: CfgOptions }, - MacroError { node: InFile>, message: String }, - UnresolvedProcMacro { node: InFile>, krate: CrateId }, + MacroError { node: InFile>, err: ExpandError }, UnresolvedMacroCall { node: InFile>, path: ModPath }, UnreachableLabel { node: InFile>, name: Name }, UndeclaredLabel { node: InFile>, name: Name }, @@ -134,16 +136,23 @@ impl Body { let data = db.function_data(f); let f = f.lookup(db); let src = f.source(db); - params = src.value.param_list().map(|param_list| { + params = src.value.param_list().map(move |param_list| { let item_tree = f.id.item_tree(db); let func = &item_tree[f.id.value]; let krate = f.container.module(db).krate; let crate_graph = db.crate_graph(); ( param_list, - func.params.clone().map(move |param| { + (0..func.params.len()).map(move |idx| { item_tree - .attrs(db, krate, param.into()) + .attrs( + db, + krate, + AttrOwner::Param( + f.id.value, + Idx::from_raw(RawIdx::from(idx as u32)), + ), + ) .is_cfg_enabled(&crate_graph[krate].cfg_options) }), ) @@ -377,6 +386,10 @@ impl BodySourceMap { self.label_map_back[label] } + pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] { + self.binding_definitions.get(&binding).map_or(&[], Deref::deref) + } + pub fn node_label(&self, node: InFile<&ast::Label>) -> Option { let src = node.map(AstPtr::new); self.label_map.get(&src).cloned() @@ -428,6 +441,7 @@ impl BodySourceMap { expansions, format_args_template_map, diagnostics, + binding_definitions, } = self; format_args_template_map.shrink_to_fit(); expr_map.shrink_to_fit(); @@ -440,5 +454,6 @@ impl BodySourceMap { pat_field_map_back.shrink_to_fit(); expansions.shrink_to_fit(); diagnostics.shrink_to_fit(); + binding_definitions.shrink_to_fit(); } } 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 be7068c807a5f..9e30aff8fe9b5 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,13 +4,13 @@ use std::mem; use base_db::CrateId; +use either::Either; use hir_expand::{ - name::{name, AsName, Name}, - ExpandError, InFile, + name::{AsName, Name}, + InFile, }; -use intern::Interned; +use intern::{sym, Interned, Symbol}; use rustc_hash::FxHashMap; -use smallvec::SmallVec; use span::AstIdMap; use stdx::never; use syntax::{ @@ -187,8 +187,10 @@ impl ExprCollector<'_> { { let is_mutable = self_param.mut_token().is_some() && self_param.amp_token().is_none(); - let binding_id: la_arena::Idx = - self.alloc_binding(name![self], BindingAnnotation::new(is_mutable, false)); + let binding_id: la_arena::Idx = self.alloc_binding( + Name::new_symbol_root(sym::self_.clone()), + BindingAnnotation::new(is_mutable, false), + ); self.body.self_param = Some(binding_id); self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); } @@ -299,7 +301,10 @@ impl ExprCollector<'_> { result_expr_id }) } - None => self.collect_block(e), + // FIXME + Some(ast::BlockModifier::AsyncGen(_)) | Some(ast::BlockModifier::Gen(_)) | None => { + self.collect_block(e) + } }, ast::Expr::LoopExpr(e) => { let label = e.label().map(|label| self.collect_label(label)); @@ -987,20 +992,11 @@ impl ExprCollector<'_> { } }; if record_diagnostics { - match &res.err { - Some(ExpandError::UnresolvedProcMacro(krate)) => { - self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro { - node: InFile::new(outer_file, syntax_ptr), - krate: *krate, - }); - } - Some(err) => { - self.source_map.diagnostics.push(BodyDiagnostic::MacroError { - node: InFile::new(outer_file, syntax_ptr), - message: err.to_string(), - }); - } - None => {} + if let Some(err) = res.err { + self.source_map.diagnostics.push(BodyDiagnostic::MacroError { + node: InFile::new(outer_file, syntax_ptr), + err, + }); } } @@ -1431,15 +1427,14 @@ impl ExprCollector<'_> { args: AstChildren, has_leading_comma: bool, binding_list: &mut BindingList, - ) -> (Box<[PatId]>, Option) { + ) -> (Box<[PatId]>, Option) { + let args: Vec<_> = args.map(|p| self.collect_pat_possibly_rest(p, binding_list)).collect(); // Find the location of the `..`, if there is one. Note that we do not // consider the possibility of there being multiple `..` here. - let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); + let ellipsis = args.iter().position(|p| p.is_right()).map(|it| it as u32); + // We want to skip the `..` pattern here, since we account for it above. - let mut args: Vec<_> = args - .filter(|p| !matches!(p, ast::Pat::RestPat(_))) - .map(|p| self.collect_pat(p, binding_list)) - .collect(); + let mut args: Vec<_> = args.into_iter().filter_map(Either::left).collect(); // if there is a leading comma, the user is most likely to type out a leading pattern // so we insert a missing pattern at the beginning for IDE features if has_leading_comma { @@ -1449,6 +1444,41 @@ impl ExprCollector<'_> { (args.into_boxed_slice(), ellipsis) } + // `collect_pat` rejects `ast::Pat::RestPat`, but it should be handled in some cases that + // it is the macro expansion result of an arg sub-pattern in a slice or tuple pattern. + fn collect_pat_possibly_rest( + &mut self, + pat: ast::Pat, + binding_list: &mut BindingList, + ) -> Either { + match &pat { + ast::Pat::RestPat(_) => Either::Right(()), + ast::Pat::MacroPat(mac) => match mac.macro_call() { + Some(call) => { + let macro_ptr = AstPtr::new(&call); + let src = self.expander.in_file(AstPtr::new(&pat)); + let pat = + self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { + if let Some(expanded_pat) = expanded_pat { + this.collect_pat_possibly_rest(expanded_pat, binding_list) + } else { + Either::Left(this.missing_pat()) + } + }); + if let Some(pat) = pat.left() { + self.source_map.pat_map.insert(src, pat); + } + pat + } + None => { + let ptr = AstPtr::new(&pat); + Either::Left(self.alloc_pat(Pat::Missing, ptr)) + } + }, + _ => Either::Left(self.collect_pat(pat, binding_list)), + } + } + // endregion: patterns /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when @@ -1473,7 +1503,7 @@ impl ExprCollector<'_> { } fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) { - self.body.bindings[binding_id].definitions.push(pat_id); + self.source_map.binding_definitions.entry(binding_id).or_default().push(pat_id); } // region: labels @@ -1588,18 +1618,22 @@ impl ExprCollector<'_> { }); let mut mappings = vec![]; let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { - Some((s, is_direct_literal)) => format_args::parse( - &s, - fmt_snippet, - args, - is_direct_literal, - |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), - |name, span| { - if let Some(span) = span { - mappings.push((span, name)) - } - }, - ), + Some((s, is_direct_literal)) => { + let call_ctx = self.expander.syntax_context(); + format_args::parse( + &s, + fmt_snippet, + args, + is_direct_literal, + |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), + |name, span| { + if let Some(span) = span { + mappings.push((span, name)) + } + }, + call_ctx, + ) + } None => FormatArgs { template: Default::default(), arguments: args.finish(), @@ -1617,30 +1651,29 @@ impl ExprCollector<'_> { } } - let lit_pieces = - fmt.template - .iter() - .enumerate() - .filter_map(|(i, piece)| { - match piece { - FormatArgsPiece::Literal(s) => Some( - self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))), - ), - &FormatArgsPiece::Placeholder(_) => { - // Inject empty string before placeholders when not already preceded by a literal piece. - if i == 0 - || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) - { - Some(self.alloc_expr_desugared(Expr::Literal(Literal::String( - "".into(), - )))) - } else { - None - } + let lit_pieces = fmt + .template + .iter() + .enumerate() + .filter_map(|(i, piece)| { + match piece { + FormatArgsPiece::Literal(s) => { + Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone())))) + } + &FormatArgsPiece::Placeholder(_) => { + // Inject empty string before placeholders when not already preceded by a literal piece. + if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) + { + Some(self.alloc_expr_desugared(Expr::Literal(Literal::String( + Symbol::empty(), + )))) + } else { + None } } - }) - .collect(); + } + }) + .collect(); let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces, is_assignee_expr: false, @@ -1723,14 +1756,18 @@ impl ExprCollector<'_> { // unsafe { ::core::fmt::UnsafeArg::new() } // ) - let Some(new_v1_formatted) = - LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted]) - else { + let Some(new_v1_formatted) = LangItem::FormatArguments.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new_v1_formatted.clone()), + ) else { return self.missing_expr(); }; - let Some(unsafe_arg_new) = - LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new]) - else { + let Some(unsafe_arg_new) = LangItem::FormatUnsafeArg.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new.clone()), + ) else { return self.missing_expr(); }; let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted)); @@ -1812,10 +1849,10 @@ impl ExprCollector<'_> { self.db, self.krate, match alignment { - Some(FormatAlignment::Left) => name![Left], - Some(FormatAlignment::Right) => name![Right], - Some(FormatAlignment::Center) => name![Center], - None => name![Unknown], + Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left.clone()), + Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right.clone()), + Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center.clone()), + None => Name::new_symbol_root(sym::Unknown.clone()), }, ); match align { @@ -1838,8 +1875,11 @@ impl ExprCollector<'_> { let width = self.make_count(width, argmap); let format_placeholder_new = { - let format_placeholder_new = - LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]); + let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::new.clone()), + ); match format_placeholder_new { Some(path) => self.alloc_expr_desugared(Expr::Path(path)), None => self.missing_expr(), @@ -1883,11 +1923,14 @@ impl ExprCollector<'_> { *n as u128, Some(BuiltinUint::Usize), ))); - let count_is = - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { - Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), - None => self.missing_expr(), - }; + let count_is = match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::Is.clone()), + ) { + Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), + None => self.missing_expr(), + }; self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]), @@ -1905,7 +1948,7 @@ impl ExprCollector<'_> { let count_param = match LangItem::FormatCount.ty_rel_path( self.db, self.krate, - name![Param], + Name::new_symbol_root(sym::Param.clone()), ) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), @@ -1921,7 +1964,11 @@ impl ExprCollector<'_> { self.missing_expr() } } - None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) { + None => match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + Name::new_symbol_root(sym::Implied.clone()), + ) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), }, @@ -1942,18 +1989,18 @@ impl ExprCollector<'_> { let new_fn = match LangItem::FormatArgument.ty_rel_path( self.db, self.krate, - match ty { - Format(Display) => name![new_display], - Format(Debug) => name![new_debug], - Format(LowerExp) => name![new_lower_exp], - Format(UpperExp) => name![new_upper_exp], - Format(Octal) => name![new_octal], - Format(Pointer) => name![new_pointer], - Format(Binary) => name![new_binary], - Format(LowerHex) => name![new_lower_hex], - Format(UpperHex) => name![new_upper_hex], - Usize => name![from_usize], - }, + Name::new_symbol_root(match ty { + Format(Display) => sym::new_display.clone(), + Format(Debug) => sym::new_debug.clone(), + Format(LowerExp) => sym::new_lower_exp.clone(), + Format(UpperExp) => sym::new_upper_exp.clone(), + Format(Octal) => sym::new_octal.clone(), + Format(Pointer) => sym::new_pointer.clone(), + Format(Binary) => sym::new_binary.clone(), + Format(LowerHex) => sym::new_lower_hex.clone(), + Format(UpperHex) => sym::new_upper_hex.clone(), + Usize => sym::from_usize.clone(), + }), ) { Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), None => self.missing_expr(), @@ -2002,12 +2049,7 @@ impl ExprCollector<'_> { } fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId { - let binding = self.body.bindings.alloc(Binding { - name, - mode, - definitions: SmallVec::new(), - problems: None, - }); + let binding = self.body.bindings.alloc(Binding { name, mode, problems: None }); if let Some(owner) = self.current_binding_owner { self.body.binding_owners.insert(binding, owner); } 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 c48d16d053049..edaee60937d15 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 @@ -517,7 +517,7 @@ impl Printer<'_> { if i != 0 { w!(self, ", "); } - if *ellipsis == Some(i) { + if *ellipsis == Some(i as u32) { w!(self, ".., "); } self.print_pat(*pat); @@ -595,7 +595,7 @@ impl Printer<'_> { if i != 0 { w!(self, ", "); } - if *ellipsis == Some(i) { + if *ellipsis == Some(i as u32) { w!(self, ", .."); } self.print_pat(*arg); 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 fd685235e17db..bf201ca834792 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 @@ -288,8 +288,9 @@ fn compute_expr_scopes( #[cfg(test)] mod tests { - use base_db::{FileId, SourceDatabase}; + use base_db::SourceDatabase; use hir_expand::{name::AsName, InFile}; + use span::FileId; use syntax::{algo::find_node_at_offset, ast, AstNode}; use test_fixture::WithFixture; use test_utils::{assert_eq_text, extract_offset}; @@ -325,7 +326,7 @@ mod tests { let file_syntax = db.parse(file_id).syntax_node(); let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); - let function = find_function(&db, file_id); + let function = find_function(&db, file_id.file_id()); let scopes = db.expr_scopes(function.into()); let (_body, source_map) = db.body_with_source_map(function.into()); @@ -338,7 +339,7 @@ mod tests { let actual = scopes .scope_chain(scope) .flat_map(|scope| scopes.entries(scope)) - .map(|it| it.name().to_smol_str()) + .map(|it| it.name().as_str()) .collect::>() .join("\n"); let expected = expected.join("\n"); @@ -480,10 +481,10 @@ fn foo() { .expect("failed to find a name at the target offset"); let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap(); - let function = find_function(&db, file_id); + let function = find_function(&db, file_id.file_id()); let scopes = db.expr_scopes(function.into()); - let (body, source_map) = db.body_with_source_map(function.into()); + let (_, source_map) = db.body_with_source_map(function.into()); let expr_scope = { let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap(); @@ -494,7 +495,7 @@ fn foo() { let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap(); let pat_src = source_map - .pat_syntax(*body.bindings[resolved.binding()].definitions.first().unwrap()) + .pat_syntax(*source_map.binding_definitions[&resolved.binding()].first().unwrap()) .unwrap(); let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs index f9e55559dab43..14b9af84e6ffb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs @@ -5,7 +5,8 @@ use std::fmt; -use hir_expand::name::{name, AsName, Name}; +use hir_expand::name::{AsName, Name}; +use intern::{sym, Symbol}; /// Different signed int types. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BuiltinInt { @@ -48,63 +49,67 @@ pub enum BuiltinType { impl BuiltinType { #[rustfmt::skip] - pub const ALL: &'static [(Name, BuiltinType)] = &[ - (name![char], BuiltinType::Char), - (name![bool], BuiltinType::Bool), - (name![str], BuiltinType::Str), - - (name![isize], BuiltinType::Int(BuiltinInt::Isize)), - (name![i8], BuiltinType::Int(BuiltinInt::I8)), - (name![i16], BuiltinType::Int(BuiltinInt::I16)), - (name![i32], BuiltinType::Int(BuiltinInt::I32)), - (name![i64], BuiltinType::Int(BuiltinInt::I64)), - (name![i128], BuiltinType::Int(BuiltinInt::I128)), - - (name![usize], BuiltinType::Uint(BuiltinUint::Usize)), - (name![u8], BuiltinType::Uint(BuiltinUint::U8)), - (name![u16], BuiltinType::Uint(BuiltinUint::U16)), - (name![u32], BuiltinType::Uint(BuiltinUint::U32)), - (name![u64], BuiltinType::Uint(BuiltinUint::U64)), - (name![u128], BuiltinType::Uint(BuiltinUint::U128)), - - (name![f16], BuiltinType::Float(BuiltinFloat::F16)), - (name![f32], BuiltinType::Float(BuiltinFloat::F32)), - (name![f64], BuiltinType::Float(BuiltinFloat::F64)), - (name![f128], BuiltinType::Float(BuiltinFloat::F128)), - ]; + pub fn all_builtin_types() -> [(Name, BuiltinType); 19] { + [ + (Name::new_symbol_root(sym::char.clone()), BuiltinType::Char), + (Name::new_symbol_root(sym::bool.clone()), BuiltinType::Bool), + (Name::new_symbol_root(sym::str.clone()), BuiltinType::Str), + + (Name::new_symbol_root(sym::isize.clone()), BuiltinType::Int(BuiltinInt::Isize)), + (Name::new_symbol_root(sym::i8.clone()), BuiltinType::Int(BuiltinInt::I8)), + (Name::new_symbol_root(sym::i16.clone()), BuiltinType::Int(BuiltinInt::I16)), + (Name::new_symbol_root(sym::i32.clone()), BuiltinType::Int(BuiltinInt::I32)), + (Name::new_symbol_root(sym::i64.clone()), BuiltinType::Int(BuiltinInt::I64)), + (Name::new_symbol_root(sym::i128.clone()), BuiltinType::Int(BuiltinInt::I128)), + + (Name::new_symbol_root(sym::usize.clone()), BuiltinType::Uint(BuiltinUint::Usize)), + (Name::new_symbol_root(sym::u8.clone()), BuiltinType::Uint(BuiltinUint::U8)), + (Name::new_symbol_root(sym::u16.clone()), BuiltinType::Uint(BuiltinUint::U16)), + (Name::new_symbol_root(sym::u32.clone()), BuiltinType::Uint(BuiltinUint::U32)), + (Name::new_symbol_root(sym::u64.clone()), BuiltinType::Uint(BuiltinUint::U64)), + (Name::new_symbol_root(sym::u128.clone()), BuiltinType::Uint(BuiltinUint::U128)), + + (Name::new_symbol_root(sym::f16.clone()), BuiltinType::Float(BuiltinFloat::F16)), + (Name::new_symbol_root(sym::f32.clone()), BuiltinType::Float(BuiltinFloat::F32)), + (Name::new_symbol_root(sym::f64.clone()), BuiltinType::Float(BuiltinFloat::F64)), + (Name::new_symbol_root(sym::f128.clone()), BuiltinType::Float(BuiltinFloat::F128)), + ] + } pub fn by_name(name: &Name) -> Option { - Self::ALL.iter().find_map(|(n, ty)| if n == name { Some(*ty) } else { None }) + Self::all_builtin_types() + .iter() + .find_map(|(n, ty)| if n == name { Some(*ty) } else { None }) } } impl AsName for BuiltinType { fn as_name(&self) -> Name { match self { - BuiltinType::Char => name![char], - BuiltinType::Bool => name![bool], - BuiltinType::Str => name![str], + BuiltinType::Char => Name::new_symbol_root(sym::char.clone()), + BuiltinType::Bool => Name::new_symbol_root(sym::bool.clone()), + BuiltinType::Str => Name::new_symbol_root(sym::str.clone()), BuiltinType::Int(it) => match it { - BuiltinInt::Isize => name![isize], - BuiltinInt::I8 => name![i8], - BuiltinInt::I16 => name![i16], - BuiltinInt::I32 => name![i32], - BuiltinInt::I64 => name![i64], - BuiltinInt::I128 => name![i128], + BuiltinInt::Isize => Name::new_symbol_root(sym::isize.clone()), + BuiltinInt::I8 => Name::new_symbol_root(sym::i8.clone()), + BuiltinInt::I16 => Name::new_symbol_root(sym::i16.clone()), + BuiltinInt::I32 => Name::new_symbol_root(sym::i32.clone()), + BuiltinInt::I64 => Name::new_symbol_root(sym::i64.clone()), + BuiltinInt::I128 => Name::new_symbol_root(sym::i128.clone()), }, BuiltinType::Uint(it) => match it { - BuiltinUint::Usize => name![usize], - BuiltinUint::U8 => name![u8], - BuiltinUint::U16 => name![u16], - BuiltinUint::U32 => name![u32], - BuiltinUint::U64 => name![u64], - BuiltinUint::U128 => name![u128], + BuiltinUint::Usize => Name::new_symbol_root(sym::usize.clone()), + BuiltinUint::U8 => Name::new_symbol_root(sym::u8.clone()), + BuiltinUint::U16 => Name::new_symbol_root(sym::u16.clone()), + BuiltinUint::U32 => Name::new_symbol_root(sym::u32.clone()), + BuiltinUint::U64 => Name::new_symbol_root(sym::u64.clone()), + BuiltinUint::U128 => Name::new_symbol_root(sym::u128.clone()), }, BuiltinType::Float(it) => match it { - BuiltinFloat::F16 => name![f16], - BuiltinFloat::F32 => name![f32], - BuiltinFloat::F64 => name![f64], - BuiltinFloat::F128 => name![f128], + BuiltinFloat::F16 => Name::new_symbol_root(sym::f16.clone()), + BuiltinFloat::F32 => Name::new_symbol_root(sym::f32.clone()), + BuiltinFloat::F64 => Name::new_symbol_root(sym::f64.clone()), + BuiltinFloat::F128 => Name::new_symbol_root(sym::f128.clone()), }, } } @@ -138,6 +143,18 @@ impl BuiltinInt { }; Some(res) } + pub fn from_suffix_sym(suffix: &Symbol) -> Option { + let res = match suffix { + s if *s == sym::isize => Self::Isize, + s if *s == sym::i8 => Self::I8, + s if *s == sym::i16 => Self::I16, + s if *s == sym::i32 => Self::I32, + s if *s == sym::i64 => Self::I64, + s if *s == sym::i128 => Self::I128, + _ => return None, + }; + Some(res) + } } #[rustfmt::skip] @@ -155,6 +172,19 @@ impl BuiltinUint { }; Some(res) } + pub fn from_suffix_sym(suffix: &Symbol) -> Option { + let res = match suffix { + s if *s == sym::usize => Self::Usize, + s if *s == sym::u8 => Self::U8, + s if *s == sym::u16 => Self::U16, + s if *s == sym::u32 => Self::U32, + s if *s == sym::u64 => Self::U64, + s if *s == sym::u128 => Self::U128, + + _ => return None, + }; + Some(res) + } } #[rustfmt::skip] 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 55043fdc4b054..c3c2e51fd0385 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -6,7 +6,8 @@ use base_db::CrateId; use hir_expand::{ name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind, }; -use intern::Interned; +use intern::{sym, Interned, Symbol}; +use la_arena::{Idx, RawIdx}; use smallvec::SmallVec; use syntax::{ast, Parse}; use triomphe::Arc; @@ -38,8 +39,8 @@ pub struct FunctionData { pub ret_type: Interned, pub attrs: Attrs, pub visibility: RawVisibility, - pub abi: Option>, - pub legacy_const_generics_indices: Box<[u32]>, + pub abi: Option, + pub legacy_const_generics_indices: Option>>, pub rustc_allow_incoherent_impl: bool, flags: FnFlags, } @@ -58,52 +59,52 @@ impl FunctionData { let crate_graph = db.crate_graph(); let cfg_options = &crate_graph[krate].cfg_options; - let enabled_params = func - .params - .clone() - .filter(|¶m| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options)); - - // If last cfg-enabled param is a `...` param, it's a varargs function. - let is_varargs = enabled_params - .clone() - .next_back() - .map_or(false, |param| item_tree[param].type_ref.is_none()); + let attr_owner = |idx| { + item_tree::AttrOwner::Param(loc.id.value, Idx::from_raw(RawIdx::from(idx as u32))) + }; let mut flags = func.flags; - if is_varargs { - flags |= FnFlags::IS_VARARGS; - } if flags.contains(FnFlags::HAS_SELF_PARAM) { // If there's a self param in the syntax, but it is cfg'd out, remove the flag. - let is_cfgd_out = match func.params.clone().next() { - Some(param) => { - !item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options) - } - None => { - stdx::never!("fn HAS_SELF_PARAM but no parameters allocated"); - true - } - }; + let is_cfgd_out = + !item_tree.attrs(db, krate, attr_owner(0usize)).is_cfg_enabled(cfg_options); if is_cfgd_out { cov_mark::hit!(cfgd_out_self_param); flags.remove(FnFlags::HAS_SELF_PARAM); } } + if flags.contains(FnFlags::IS_VARARGS) { + if let Some((_, param)) = func.params.iter().enumerate().rev().find(|&(idx, _)| { + item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options) + }) { + if param.type_ref.is_some() { + flags.remove(FnFlags::IS_VARARGS); + } + } else { + flags.remove(FnFlags::IS_VARARGS); + } + } let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let legacy_const_generics_indices = attrs - .by_key("rustc_legacy_const_generics") + .by_key(&sym::rustc_legacy_const_generics) .tt_values() .next() .map(parse_rustc_legacy_const_generics) - .unwrap_or_default(); - let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists(); + .filter(|it| !it.is_empty()) + .map(Box::new); + let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); Arc::new(FunctionData { name: func.name.clone(), - params: enabled_params - .clone() - .filter_map(|id| item_tree[id].type_ref.clone()) + params: func + .params + .iter() + .enumerate() + .filter(|&(idx, _)| { + item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options) + }) + .filter_map(|(_, param)| param.type_ref.clone()) .collect(), ret_type: func.ret_type.clone(), attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), @@ -150,7 +151,7 @@ fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> { let mut indices = Vec::new(); for args in tt.token_trees.chunks(2) { match &args[0] { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.text.parse() { + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { Ok(index) => indices.push(index), Err(_) => break, }, @@ -200,8 +201,8 @@ impl TypeAliasData { ModItem::from(loc.id.value).into(), ); let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let rustc_allow_incoherent_impl = attrs.by_key("rustc_allow_incoherent_impl").exists(); + attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); + let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); Arc::new(TypeAliasData { name: typ.name.clone(), @@ -223,6 +224,7 @@ pub struct TraitData { pub is_unsafe: bool, pub rustc_has_incoherent_inherent_impls: bool, pub skip_array_during_method_dispatch: bool, + pub skip_boxed_slice_during_method_dispatch: bool, pub fundamental: bool, pub visibility: RawVisibility, /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore @@ -250,11 +252,20 @@ impl TraitData { let is_unsafe = tr_def.is_unsafe; let visibility = item_tree[tr_def.visibility].clone(); let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()); - let skip_array_during_method_dispatch = - attrs.by_key("rustc_skip_array_during_method_dispatch").exists(); + let mut skip_array_during_method_dispatch = + attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists(); + let mut skip_boxed_slice_during_method_dispatch = false; + for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() { + for tt in tt.token_trees.iter() { + if let crate::tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt { + skip_array_during_method_dispatch |= ident.sym == sym::array; + skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice; + } + } + } let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let fundamental = attrs.by_key("fundamental").exists(); + attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); + let fundamental = attrs.by_key(&sym::fundamental).exists(); let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); @@ -269,6 +280,7 @@ impl TraitData { is_unsafe, visibility, skip_array_during_method_dispatch, + skip_boxed_slice_during_method_dispatch, rustc_has_incoherent_inherent_impls, fundamental, }), @@ -393,7 +405,7 @@ impl Macro2Data { let helpers = item_tree .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) - .by_key("rustc_builtin_macro") + .by_key(&sym::rustc_builtin_macro) .tt_values() .next() .and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees)) @@ -423,7 +435,7 @@ impl MacroRulesData { let macro_export = item_tree .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) - .by_key("macro_export") + .by_key(&sym::macro_export) .exists(); Arc::new(MacroRulesData { name: makro.name.clone(), macro_export }) @@ -485,7 +497,7 @@ impl ExternCrateDeclData { let name = extern_crate.name.clone(); let krate = loc.container.krate(); - let crate_id = if name == hir_expand::name![self] { + let crate_id = if name == sym::self_.clone() { Some(krate) } else { db.crate_def_map(krate) @@ -526,7 +538,7 @@ impl ConstData { let rustc_allow_incoherent_impl = item_tree .attrs(db, loc.container.module(db).krate(), ModItem::from(loc.id.value).into()) - .by_key("rustc_allow_incoherent_impl") + .by_key(&sym::rustc_allow_incoherent_impl) .exists(); Arc::new(ConstData { @@ -618,7 +630,8 @@ impl<'a> AssocItemCollector<'a> { if !attrs.is_cfg_enabled(self.expander.cfg_options()) { self.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id.local_id, - InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).erase()), + tree_id, + ModItem::from(item).into(), attrs.cfg().unwrap(), self.expander.cfg_options().clone(), )); @@ -644,22 +657,18 @@ impl<'a> AssocItemCollector<'a> { // crate failed), skip expansion like we would if it was // disabled. This is analogous to the handling in // `DefCollector::collect_macros`. - if exp.is_dummy() { - self.diagnostics.push(DefDiagnostic::unresolved_proc_macro( + if let Some(err) = exp.as_expand_error(self.module_id.krate) { + self.diagnostics.push(DefDiagnostic::macro_error( self.module_id.local_id, - loc.kind, - loc.def.krate, + ast_id, + (*attr.path).clone(), + err, )); - - continue 'attrs; - } - if exp.is_disabled() { continue 'attrs; } } self.macro_calls.push((ast_id, call_id)); - let res = self.expander.enter_expand_id::(self.db, call_id); self.collect_macro_items(res); 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 0fe73418e51fc..a70710e565c27 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 @@ -5,24 +5,20 @@ use bitflags::bitflags; use cfg::CfgOptions; use either::Either; -use hir_expand::{ - name::{AsName, Name}, - HirFileId, InFile, -}; -use intern::Interned; +use hir_expand::name::Name; +use intern::{sym, Interned}; use la_arena::Arena; use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; -use syntax::ast::{self, HasName, HasVisibility}; use triomphe::Arc; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, - item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, + item_tree::{ + AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId, + }, lang_item::LangItem, - lower::LowerCtx, nameres::diagnostics::{DefDiagnostic, DefDiagnostics}, - trace::Trace, tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, type_ref::TypeRef, visibility::RawVisibility, @@ -95,7 +91,7 @@ fn repr_from_value( item_tree: &ItemTree, of: AttrOwner, ) -> Option { - item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt) + item_tree.attrs(db, krate, of).by_key(&sym::repr).tt_values().find_map(parse_repr_tt) } fn parse_repr_tt(tt: &Subtree) -> Option { @@ -112,12 +108,12 @@ fn parse_repr_tt(tt: &Subtree) -> Option { let mut tts = tt.token_trees.iter().peekable(); while let Some(tt) = tts.next() { if let TokenTree::Leaf(Leaf::Ident(ident)) = tt { - flags.insert(match &*ident.text { - "packed" => { + flags.insert(match &ident.sym { + s if *s == sym::packed => { let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() { tts.next(); if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { - lit.text.parse().unwrap_or_default() + lit.symbol.as_str().parse().unwrap_or_default() } else { 0 } @@ -129,11 +125,11 @@ fn parse_repr_tt(tt: &Subtree) -> Option { Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { pack }); ReprFlags::empty() } - "align" => { + s if *s == sym::align => { if let Some(TokenTree::Subtree(tt)) = tts.peek() { tts.next(); if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { - if let Ok(align) = lit.text.parse() { + if let Ok(align) = lit.symbol.as_str().parse() { let align = Align::from_bytes(align).ok(); max_align = max_align.max(align); } @@ -141,13 +137,13 @@ fn parse_repr_tt(tt: &Subtree) -> Option { } ReprFlags::empty() } - "C" => ReprFlags::IS_C, - "transparent" => ReprFlags::IS_TRANSPARENT, - "simd" => ReprFlags::IS_SIMD, + s if *s == sym::C => ReprFlags::IS_C, + s if *s == sym::transparent => ReprFlags::IS_TRANSPARENT, + s if *s == sym::simd => ReprFlags::IS_SIMD, repr => { - if let Some(builtin) = BuiltinInt::from_suffix(repr) + if let Some(builtin) = BuiltinInt::from_suffix_sym(repr) .map(Either::Left) - .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right)) + .or_else(|| BuiltinUint::from_suffix_sym(repr).map(Either::Right)) { int = Some(match builtin { Either::Left(bi) => match bi { @@ -194,10 +190,10 @@ impl StructData { 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() { + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; } - if attrs.by_key("fundamental").exists() { + if attrs.by_key(&sym::fundamental).exists() { flags |= StructFlags::IS_FUNDAMENTAL; } if let Some(lang) = attrs.lang_item() { @@ -211,20 +207,25 @@ impl StructData { } let strukt = &item_tree[loc.id.value]; - let (variant_data, diagnostics) = lower_fields( + let (data, diagnostics) = lower_fields( db, krate, - loc.id.file_id(), loc.container.local_id, + loc.id.tree_id(), &item_tree, &db.crate_graph()[krate].cfg_options, + FieldParent::Struct(loc.id.value), &strukt.fields, None, ); ( Arc::new(StructData { name: strukt.name.clone(), - variant_data: Arc::new(variant_data), + variant_data: Arc::new(match strukt.shape { + FieldsShape::Record => VariantData::Record(data), + FieldsShape::Tuple => VariantData::Tuple(data), + FieldsShape::Unit => VariantData::Unit, + }), repr, visibility: item_tree[strukt.visibility].clone(), flags, @@ -248,28 +249,29 @@ impl StructData { let repr = repr_from_value(db, krate, &item_tree, 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() { + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; } - if attrs.by_key("fundamental").exists() { + if attrs.by_key(&sym::fundamental).exists() { flags |= StructFlags::IS_FUNDAMENTAL; } let union = &item_tree[loc.id.value]; - let (variant_data, diagnostics) = lower_fields( + let (data, diagnostics) = lower_fields( db, krate, - loc.id.file_id(), loc.container.local_id, + loc.id.tree_id(), &item_tree, &db.crate_graph()[krate].cfg_options, + FieldParent::Union(loc.id.value), &union.fields, None, ); ( Arc::new(StructData { name: union.name.clone(), - variant_data: Arc::new(variant_data), + variant_data: Arc::new(VariantData::Record(data)), repr, visibility: item_tree[union.visibility].clone(), flags, @@ -287,7 +289,7 @@ impl EnumData { let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let rustc_has_incoherent_inherent_impls = item_tree .attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()) - .by_key("rustc_has_incoherent_inherent_impls") + .by_key(&sym::rustc_has_incoherent_inherent_impls) .exists(); let enum_ = &item_tree[loc.id.value]; @@ -336,13 +338,14 @@ impl EnumVariantData { let item_tree = loc.id.item_tree(db); let variant = &item_tree[loc.id.value]; - let (var_data, diagnostics) = lower_fields( + let (data, diagnostics) = lower_fields( db, krate, - loc.id.file_id(), container.local_id, + loc.id.tree_id(), &item_tree, &db.crate_graph()[krate].cfg_options, + FieldParent::Variant(loc.id.value), &variant.fields, Some(item_tree[loc.parent.lookup(db).id.value].visibility), ); @@ -350,7 +353,11 @@ impl EnumVariantData { ( Arc::new(EnumVariantData { name: variant.name.clone(), - variant_data: Arc::new(var_data), + variant_data: Arc::new(match variant.shape { + FieldsShape::Record => VariantData::Record(data), + FieldsShape::Tuple => VariantData::Tuple(data), + FieldsShape::Unit => VariantData::Unit, + }), }), DefDiagnostics::new(diagnostics), ) @@ -396,123 +403,35 @@ pub enum StructKind { Unit, } -pub(crate) fn lower_struct( - db: &dyn DefDatabase, - trace: &mut Trace>, - ast: &InFile, - krate: CrateId, - item_tree: &ItemTree, - fields: &Fields, -) -> StructKind { - let ctx = LowerCtx::new(db, ast.file_id); - - match (&ast.value, fields) { - (ast::StructKind::Tuple(fl), Fields::Tuple(fields)) => { - let cfg_options = &db.crate_graph()[krate].cfg_options; - for ((i, fd), item_tree_id) in fl.fields().enumerate().zip(fields.clone()) { - if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) { - continue; - } - - trace.alloc( - || Either::Left(fd.clone()), - || FieldData { - name: Name::new_tuple_field(i), - type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())), - visibility: RawVisibility::from_ast(db, fd.visibility(), &mut |range| { - ctx.span_map().span_for_range(range).ctx - }), - }, - ); - } - StructKind::Tuple - } - (ast::StructKind::Record(fl), Fields::Record(fields)) => { - let cfg_options = &db.crate_graph()[krate].cfg_options; - for (fd, item_tree_id) in fl.fields().zip(fields.clone()) { - if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) { - continue; - } - - trace.alloc( - || Either::Right(fd.clone()), - || FieldData { - name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), - type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())), - visibility: RawVisibility::from_ast(db, fd.visibility(), &mut |range| { - ctx.span_map().span_for_range(range).ctx - }), - }, - ); - } - StructKind::Record - } - _ => StructKind::Unit, - } -} - fn lower_fields( db: &dyn DefDatabase, krate: CrateId, - current_file_id: HirFileId, container: LocalModuleId, + tree_id: TreeId, item_tree: &ItemTree, cfg_options: &CfgOptions, - fields: &Fields, + parent: FieldParent, + fields: &[Field], override_visibility: Option, -) -> (VariantData, Vec) { +) -> (Arena, Vec) { let mut diagnostics = Vec::new(); - match fields { - Fields::Record(flds) => { - let mut arena = Arena::new(); - for field_id in flds.clone() { - let attrs = item_tree.attrs(db, krate, field_id.into()); - let field = &item_tree[field_id]; - if attrs.is_cfg_enabled(cfg_options) { - arena.alloc(lower_field(item_tree, field, override_visibility)); - } else { - diagnostics.push(DefDiagnostic::unconfigured_code( - container, - InFile::new( - current_file_id, - match field.ast_id { - FieldAstId::Record(it) => it.erase(), - FieldAstId::Tuple(it) => it.erase(), - }, - ), - attrs.cfg().unwrap(), - cfg_options.clone(), - )) - } - } - (VariantData::Record(arena), diagnostics) - } - Fields::Tuple(flds) => { - let mut arena = Arena::new(); - for field_id in flds.clone() { - let attrs = item_tree.attrs(db, krate, field_id.into()); - let field = &item_tree[field_id]; - if attrs.is_cfg_enabled(cfg_options) { - arena.alloc(lower_field(item_tree, field, override_visibility)); - } else { - diagnostics.push(DefDiagnostic::unconfigured_code( - container, - InFile::new( - current_file_id, - match field.ast_id { - FieldAstId::Record(it) => it.erase(), - FieldAstId::Tuple(it) => it.erase(), - }, - ), - attrs.cfg().unwrap(), - cfg_options.clone(), - )) - } - } - (VariantData::Tuple(arena), diagnostics) + let mut arena = Arena::new(); + for (idx, field) in fields.iter().enumerate() { + let attr_owner = AttrOwner::make_field_indexed(parent, idx); + let attrs = item_tree.attrs(db, krate, attr_owner); + if attrs.is_cfg_enabled(cfg_options) { + arena.alloc(lower_field(item_tree, field, override_visibility)); + } else { + diagnostics.push(DefDiagnostic::unconfigured_code( + container, + tree_id, + attr_owner, + attrs.cfg().unwrap(), + cfg_options.clone(), + )) } - Fields::Unit => (VariantData::Unit, diagnostics), } + (arena, diagnostics) } fn lower_field( 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 0eb9e7d30b251..56feb0163e13a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -1,10 +1,10 @@ //! Defines database & queries for name resolution. -use base_db::{salsa, CrateId, FileId, SourceDatabase, Upcast}; +use base_db::{salsa, CrateId, SourceDatabase, Upcast}; use either::Either; use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId}; -use intern::Interned; +use intern::{sym, Interned}; use la_arena::ArenaMap; -use span::MacroCallId; +use span::{EditionedFileId, MacroCallId}; use syntax::{ast, AstPtr}; use triomphe::Arc; @@ -177,6 +177,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast (Arc, Arc); #[salsa::invoke(Body::body_query)] @@ -239,11 +240,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast bool; - fn include_macro_invoc(&self, crate_id: CrateId) -> Vec<(MacroCallId, FileId)>; + fn include_macro_invoc(&self, crate_id: CrateId) -> Vec<(MacroCallId, EditionedFileId)>; } // return: macro call id and include file id -fn include_macro_invoc(db: &dyn DefDatabase, krate: CrateId) -> Vec<(MacroCallId, FileId)> { +fn include_macro_invoc( + db: &dyn DefDatabase, + krate: CrateId, +) -> Vec<(MacroCallId, EditionedFileId)> { db.crate_def_map(krate) .modules .values() @@ -257,13 +261,13 @@ fn include_macro_invoc(db: &dyn DefDatabase, krate: CrateId) -> Vec<(MacroCallId } fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { - let file = db.crate_graph()[crate_id].root_file_id; + let file = db.crate_graph()[crate_id].root_file_id(); let item_tree = db.file_item_tree(file.into()); let attrs = item_tree.raw_attrs(AttrOwner::TopLevel); for attr in &**attrs { - match attr.path().as_ident().and_then(|id| id.as_text()) { - Some(ident) if ident == "no_std" => return true, - Some(ident) if ident == "cfg_attr" => {} + match attr.path().as_ident() { + Some(ident) if *ident == sym::no_std.clone() => return true, + Some(ident) if *ident == sym::cfg_attr.clone() => {} _ => continue, } @@ -278,7 +282,7 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { tt.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); for output in segments.skip(1) { match output { - [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "no_std" => { + [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => { return true } _ => {} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs index dbf8e6b225c2a..6d8b4445f75bc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs @@ -6,10 +6,11 @@ use base_db::CrateId; use cfg::CfgOptions; use drop_bomb::DropBomb; use hir_expand::{ - attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandResult, HirFileId, - InFile, MacroCallId, + attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandErrorKind, + ExpandResult, HirFileId, InFile, Lookup, MacroCallId, }; use limit::Limit; +use span::SyntaxContextId; use syntax::{ast, Parse}; use triomphe::Arc; @@ -52,6 +53,11 @@ impl Expander { self.module.krate } + pub fn syntax_context(&self) -> SyntaxContextId { + // FIXME: + SyntaxContextId::ROOT + } + pub fn enter_expand( &mut self, db: &dyn DefDatabase, @@ -154,26 +160,30 @@ impl Expander { // so don't return overflow error here to avoid diagnostics duplication. cov_mark::hit!(overflow_but_not_me); return ExpandResult::ok(None); - } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() { - self.recursion_depth = u32::MAX; - cov_mark::hit!(your_stack_belongs_to_me); - return ExpandResult::only_err(ExpandError::RecursionOverflow); } let ExpandResult { value, err } = op(self); let Some(call_id) = value else { return ExpandResult { value: None, err }; }; + if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() { + self.recursion_depth = u32::MAX; + cov_mark::hit!(your_stack_belongs_to_me); + return ExpandResult::only_err(ExpandError::new( + db.macro_arg_considering_derives(call_id, &call_id.lookup(db.upcast()).kind).2, + ExpandErrorKind::RecursionOverflow, + )); + } let macro_file = call_id.as_macro_file(); let res = db.parse_macro_expansion(macro_file); let err = err.or(res.err); ExpandResult { - value: match err { + value: match &err { // If proc-macro is disabled or unresolved, we want to expand to a missing expression // instead of an empty tree which might end up in an empty block. - Some(ExpandError::UnresolvedProcMacro(_)) => None, + Some(e) if matches!(e.kind(), ExpandErrorKind::MissingProcMacroExpander(_)) => None, _ => (|| { let parse = res.value.0.cast::()?; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 9a3c0495414ca..91594aecd0441 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -2,10 +2,12 @@ use std::{cell::Cell, cmp::Ordering, iter}; +use base_db::{CrateId, CrateOrigin, LangCrateOrigin}; use hir_expand::{ - name::{known, AsName, Name}, + name::{AsName, Name}, Lookup, }; +use intern::sym; use rustc_hash::FxHashSet; use crate::{ @@ -36,7 +38,8 @@ pub fn find_path( // within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so // default to plain paths. - if item.module(db).is_some_and(ModuleId::is_within_block) { + let item_module = item.module(db)?; + if item_module.is_within_block() { prefix_kind = PrefixKind::Plain; } cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate()); @@ -53,23 +56,17 @@ pub fn find_path( }, item, MAX_PATH_LEN, + db.crate_graph()[item_module.krate()].origin.is_lang(), ) } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum Stability { Unstable, Stable, } use Stability::*; -fn zip_stability(a: Stability, b: Stability) -> Stability { - match (a, b) { - (Stable, Stable) => Stable, - _ => Unstable, - } -} - const MAX_PATH_LEN: usize = 15; const FIND_PATH_FUEL: usize = 10000; @@ -107,12 +104,18 @@ struct FindPathCtx<'db> { } /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId -fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option { +fn find_path_inner( + ctx: &FindPathCtx<'_>, + item: ItemInNs, + max_len: usize, + is_std_item: bool, +) -> Option { // - if the item is a module, jump straight to module search - if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { - let mut visited_modules = FxHashSet::default(); - return find_path_for_module(ctx, &mut visited_modules, module_id, max_len) - .map(|(item, _)| item); + if !is_std_item { + if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { + return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len) + .map(|choice| choice.path); + } } let may_be_in_scope = match ctx.prefix { @@ -130,14 +133,17 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt // - if the item is in the prelude, return the name from there if let Some(value) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) { - return Some(value); + return Some(value.path); } if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { // - if the item is an enum variant, refer to it via the enum - if let Some(mut path) = - find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len) - { + if let Some(mut path) = find_path_inner( + ctx, + ItemInNs::Types(variant.lookup(ctx.db).parent.into()), + max_len, + is_std_item, + ) { path.push_segment(ctx.db.enum_variant_data(variant).name.clone()); return Some(path); } @@ -146,22 +152,42 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt // variant somewhere } - let mut visited_modules = FxHashSet::default(); + if is_std_item { + // The item we are searching for comes from the sysroot libraries, so skip prefer looking in + // the sysroot libraries directly. + // We do need to fallback as the item in question could be re-exported by another crate + // while not being a transitive dependency of the current crate. + if let Some(choice) = find_in_sysroot(ctx, &mut FxHashSet::default(), item, max_len) { + return Some(choice.path); + } + } - calculate_best_path(ctx, &mut visited_modules, item, max_len).map(|(item, _)| item) + let mut best_choice = None; + calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice); + best_choice.map(|choice| choice.path) } #[tracing::instrument(skip_all)] fn find_path_for_module( ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, module_id: ModuleId, + maybe_extern: bool, max_len: usize, -) -> Option<(ModPath, Stability)> { +) -> Option { + if max_len == 0 { + // recursive base case, we can't find a path of length 0 + return None; + } if let Some(crate_root) = module_id.as_crate_root() { - if crate_root == ctx.from.derive_crate_root() { + if !maybe_extern || crate_root == ctx.from.derive_crate_root() { // - if the item is the crate root, return `crate` - return Some((ModPath::from_segments(PathKind::Crate, None), Stable)); + return Some(Choice { + path: ModPath::from_segments(PathKind::Crate, None), + path_text_len: 5, + stability: Stable, + prefer_due_to_prelude: false, + }); } // - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude @@ -188,7 +214,7 @@ fn find_path_for_module( } else { PathKind::Plain }; - return Some((ModPath::from_segments(kind, iter::once(name.clone())), Stable)); + return Some(Choice::new(ctx.cfg.prefer_prelude, kind, name.clone(), Stable)); } } @@ -206,27 +232,39 @@ fn find_path_for_module( ); if let Some(scope_name) = scope_name { // - if the item is already in scope, return the name under which it is - return Some(( - ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name)), + return Some(Choice::new( + ctx.cfg.prefer_prelude, + ctx.prefix.path_kind(), + scope_name, Stable, )); } } // - if the module can be referenced as self, super or crate, do that - if let Some(mod_path) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) { - if ctx.prefix != PrefixKind::ByCrate || mod_path.kind == PathKind::Crate { - return Some((mod_path, Stable)); + if let Some(kind) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) { + if ctx.prefix != PrefixKind::ByCrate || kind == PathKind::Crate { + return Some(Choice { + path: ModPath::from_segments(kind, None), + path_text_len: path_kind_len(kind), + stability: Stable, + prefer_due_to_prelude: false, + }); } } // - if the module is in the prelude, return it by that path - if let Some(mod_path) = - find_in_prelude(ctx.db, ctx.from_def_map, ItemInNs::Types(module_id.into()), ctx.from) - { - return Some((mod_path, Stable)); + let item = ItemInNs::Types(module_id.into()); + if let Some(choice) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) { + return Some(choice); + } + let mut best_choice = None; + if maybe_extern { + calculate_best_path(ctx, visited_modules, item, max_len, &mut best_choice); + } else { + calculate_best_path_local(ctx, visited_modules, item, max_len, &mut best_choice); } - calculate_best_path(ctx, visited_modules, ItemInNs::Types(module_id.into()), max_len) + best_choice } fn find_in_scope( @@ -251,7 +289,7 @@ fn find_in_prelude( local_def_map: &DefMap, item: ItemInNs, from: ModuleId, -) -> Option { +) -> Option { let (prelude_module, _) = local_def_map.prelude()?; let prelude_def_map = prelude_module.def_map(db); let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; @@ -273,7 +311,7 @@ fn find_in_prelude( }); if found_and_same_def.unwrap_or(true) { - Some(ModPath::from_segments(PathKind::Plain, iter::once(name.clone()))) + Some(Choice::new(false, PathKind::Plain, name.clone(), Stable)) } else { None } @@ -283,7 +321,7 @@ fn is_kw_kind_relative_to_from( def_map: &DefMap, item: ModuleId, from: ModuleId, -) -> Option { +) -> Option { if item.krate != from.krate || item.is_within_block() || from.is_within_block() { return None; } @@ -291,14 +329,11 @@ fn is_kw_kind_relative_to_from( let from = from.local_id; if item == from { // - if the item is the module we're in, use `self` - Some(ModPath::from_segments(PathKind::SELF, None)) + Some(PathKind::SELF) } else if let Some(parent_id) = def_map[from].parent { if item == parent_id { // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) - Some(ModPath::from_segments( - if parent_id == DefMap::ROOT { PathKind::Crate } else { PathKind::Super(1) }, - None, - )) + Some(if parent_id == DefMap::ROOT { PathKind::Crate } else { PathKind::Super(1) }) } else { None } @@ -310,15 +345,11 @@ fn is_kw_kind_relative_to_from( #[tracing::instrument(skip_all)] fn calculate_best_path( ctx: &FindPathCtx<'_>, - visited_modules: &mut FxHashSet, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, item: ItemInNs, max_len: usize, -) -> Option<(ModPath, Stability)> { - if max_len <= 1 { - // recursive base case, we can't find a path prefix of length 0, one segment is occupied by - // the item's name itself. - return None; - } + best_choice: &mut Option, +) { let fuel = ctx.fuel.get(); if fuel == 0 { // we ran out of fuel, so we stop searching here @@ -327,144 +358,208 @@ fn calculate_best_path( item.krate(ctx.db), ctx.from.krate() ); - return None; + return; } ctx.fuel.set(fuel - 1); - let mut best_path = None; - let mut best_path_len = max_len; - let mut process = |mut path: (ModPath, Stability), name, best_path_len: &mut _| { - path.0.push_segment(name); - let new_path = match best_path.take() { - Some(best_path) => select_best_path(best_path, path, ctx.cfg), - None => path, - }; - if new_path.1 == Stable { - *best_path_len = new_path.0.len(); - } - match &mut best_path { - Some((old_path, old_stability)) => { - *old_path = new_path.0; - *old_stability = zip_stability(*old_stability, new_path.1); - } - None => best_path = Some(new_path), - } - }; - let db = ctx.db; - if item.krate(db) == Some(ctx.from.krate) { + if item.krate(ctx.db) == Some(ctx.from.krate) { // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. - // FIXME: cache the `find_local_import_locations` output? - find_local_import_locations(db, item, ctx.from, ctx.from_def_map, |name, module_id| { - if !visited_modules.insert(module_id) { - return; - } - // we are looking for paths of length up to best_path_len, any longer will make it be - // less optimal. The -1 is due to us pushing name onto it afterwards. - if let Some(path) = - find_path_for_module(ctx, visited_modules, module_id, best_path_len - 1) - { - process(path, name.clone(), &mut best_path_len); - } - }) + calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice) } else { // Item was defined in some upstream crate. This means that it must be exported from one, // too (unless we can't name it at all). It could *also* be (re)exported by the same crate // that wants to import it here, but we always prefer to use the external path here. - for dep in &db.crate_graph()[ctx.from.krate].dependencies { - let import_map = db.import_map(dep.crate_id); - let Some(import_info_for) = import_map.import_info_for(item) else { continue }; - for info in import_info_for { - if info.is_doc_hidden { - // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate - continue; - } - - // Determine best path for containing module and append last segment from `info`. - // FIXME: we should guide this to look up the path locally, or from the same crate again? - let path = - find_path_for_module(ctx, visited_modules, info.container, best_path_len - 1); - let Some((path, path_stability)) = path else { - continue; - }; - cov_mark::hit!(partially_imported); - let path = ( - path, - zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }), - ); + ctx.db.crate_graph()[ctx.from.krate].dependencies.iter().for_each(|dep| { + find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id) + }); + } +} - process(path, info.name.clone(), &mut best_path_len); +fn find_in_sysroot( + ctx: &FindPathCtx<'_>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, + item: ItemInNs, + max_len: usize, +) -> Option { + let crate_graph = ctx.db.crate_graph(); + let dependencies = &crate_graph[ctx.from.krate].dependencies; + let mut best_choice = None; + let mut search = |lang, best_choice: &mut _| { + if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| { + match crate_graph[dep.crate_id].origin { + CrateOrigin::Lang(l) => l == lang, + _ => false, } + }) { + find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id); + } + }; + if ctx.cfg.prefer_no_std { + search(LangCrateOrigin::Core, &mut best_choice); + if matches!(best_choice, Some(Choice { stability: Stable, .. })) { + return best_choice; + } + search(LangCrateOrigin::Std, &mut best_choice); + if matches!(best_choice, Some(Choice { stability: Stable, .. })) { + return best_choice; + } + } else { + search(LangCrateOrigin::Std, &mut best_choice); + if matches!(best_choice, Some(Choice { stability: Stable, .. })) { + return best_choice; + } + search(LangCrateOrigin::Core, &mut best_choice); + if matches!(best_choice, Some(Choice { stability: Stable, .. })) { + return best_choice; } } - best_path + let mut best_choice = None; + dependencies.iter().filter(|it| it.is_sysroot()).for_each(|dep| { + find_in_dep(ctx, visited_modules, item, max_len, &mut best_choice, dep.crate_id); + }); + best_choice } -/// Select the best (most relevant) path between two paths. -/// This accounts for stability, path length whether, std should be chosen over alloc/core paths as -/// well as ignoring prelude like paths or not. -fn select_best_path( - old_path @ (_, old_stability): (ModPath, Stability), - new_path @ (_, new_stability): (ModPath, Stability), - cfg: ImportPathConfig, -) -> (ModPath, Stability) { - match (old_stability, new_stability) { - (Stable, Unstable) => return old_path, - (Unstable, Stable) => return new_path, - _ => {} - } - const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc]; - - let choose = |new: (ModPath, _), old: (ModPath, _)| { - let (new_path, _) = &new; - let (old_path, _) = &old; - let new_has_prelude = new_path.segments().iter().any(|seg| seg == &known::prelude); - let old_has_prelude = old_path.segments().iter().any(|seg| seg == &known::prelude); - match (new_has_prelude, old_has_prelude, cfg.prefer_prelude) { - (true, false, true) | (false, true, false) => new, - (true, false, false) | (false, true, true) => old, - // no prelude difference in the paths, so pick the shorter one - (true, true, _) | (false, false, _) => { - let new_path_is_shorter = new_path - .len() - .cmp(&old_path.len()) - .then_with(|| new_path.textual_len().cmp(&old_path.textual_len())) - .is_lt(); - if new_path_is_shorter { - new - } else { - old - } +fn find_in_dep( + ctx: &FindPathCtx<'_>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, + item: ItemInNs, + max_len: usize, + best_choice: &mut Option, + dep: CrateId, +) { + let import_map = ctx.db.import_map(dep); + let Some(import_info_for) = import_map.import_info_for(item) else { + return; + }; + for info in import_info_for { + if info.is_doc_hidden { + // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate + continue; + } + + // Determine best path for containing module and append last segment from `info`. + // FIXME: we should guide this to look up the path locally, or from the same crate again? + let choice = find_path_for_module( + ctx, + visited_modules, + info.container, + true, + best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, + ); + let Some(mut choice) = choice else { + continue; + }; + cov_mark::hit!(partially_imported); + if info.is_unstable { + choice.stability = Unstable; + } + + Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, info.name.clone()); + } +} + +fn calculate_best_path_local( + ctx: &FindPathCtx<'_>, + visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, + item: ItemInNs, + max_len: usize, + best_choice: &mut Option, +) { + // FIXME: cache the `find_local_import_locations` output? + find_local_import_locations( + ctx.db, + item, + ctx.from, + ctx.from_def_map, + visited_modules, + |visited_modules, name, module_id| { + // we are looking for paths of length up to best_path_len, any longer will make it be + // less optimal. The -1 is due to us pushing name onto it afterwards. + if let Some(choice) = find_path_for_module( + ctx, + visited_modules, + module_id, + false, + best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, + ) { + Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone()); } + }, + ); +} + +struct Choice { + path: ModPath, + /// The length in characters of the path + path_text_len: usize, + /// The stability of the path + stability: Stability, + /// Whether this path contains a prelude segment and preference for it has been signaled + prefer_due_to_prelude: bool, +} + +impl Choice { + fn new(prefer_prelude: bool, kind: PathKind, name: Name, stability: Stability) -> Self { + Self { + path_text_len: path_kind_len(kind) + name.as_str().len(), + stability, + prefer_due_to_prelude: prefer_prelude && name == sym::prelude, + path: ModPath::from_segments(kind, iter::once(name)), } - }; + } - match (old_path.0.segments().first(), new_path.0.segments().first()) { - (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { - let rank = match cfg.prefer_no_std { - false => |name: &Name| match name { - name if name == &known::core => 0, - name if name == &known::alloc => 1, - name if name == &known::std => 2, - _ => unreachable!(), - }, - true => |name: &Name| match name { - name if name == &known::core => 2, - name if name == &known::alloc => 1, - name if name == &known::std => 0, - _ => unreachable!(), - }, - }; - let nrank = rank(new); - let orank = rank(old); - match nrank.cmp(&orank) { - Ordering::Less => old_path, - Ordering::Equal => choose(new_path, old_path), - Ordering::Greater => new_path, + fn push(mut self, prefer_prelude: bool, name: Name) -> Self { + self.path_text_len += name.as_str().len(); + self.prefer_due_to_prelude |= prefer_prelude && name == sym::prelude; + self.path.push_segment(name); + self + } + + fn try_select( + current: &mut Option, + mut other: Choice, + prefer_prelude: bool, + name: Name, + ) { + let Some(current) = current else { + *current = Some(other.push(prefer_prelude, name)); + return; + }; + match other + .stability + .cmp(¤t.stability) + .then_with(|| other.prefer_due_to_prelude.cmp(¤t.prefer_due_to_prelude)) + .then_with(|| (current.path.len()).cmp(&(other.path.len() + 1))) + { + Ordering::Less => return, + Ordering::Equal => { + other.path_text_len += name.as_str().len(); + if let Ordering::Less | Ordering::Equal = + current.path_text_len.cmp(&other.path_text_len) + { + return; + } + } + Ordering::Greater => { + other.path_text_len += name.as_str().len(); } } - _ => choose(new_path, old_path), + other.path.push_segment(name); + *current = other; + } +} + +fn path_kind_len(kind: PathKind) -> usize { + match kind { + PathKind::Plain => 0, + PathKind::Super(0) => 4, + PathKind::Super(s) => s as usize * 5, + PathKind::Crate => 5, + PathKind::Abs => 2, + PathKind::DollarCrate(_) => 0, } } @@ -474,7 +569,8 @@ fn find_local_import_locations( item: ItemInNs, from: ModuleId, def_map: &DefMap, - mut cb: impl FnMut(&Name, ModuleId), + visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>, + mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId), ) { let _p = tracing::info_span!("find_local_import_locations").entered(); @@ -487,32 +583,29 @@ fn find_local_import_locations( let mut worklist = def_map[from.local_id] .children .values() - .map(|child| def_map.module_id(*child)) - // FIXME: do we need to traverse out of block expressions here? + .map(|&child| def_map.module_id(child)) .chain(iter::successors(from.containing_module(db), |m| m.containing_module(db))) + .zip(iter::repeat(false)) .collect::>(); - let mut seen: FxHashSet<_> = FxHashSet::default(); let def_map = def_map.crate_root().def_map(db); - - while let Some(module) = worklist.pop() { - if !seen.insert(module) { - continue; // already processed this module + let mut block_def_map; + let mut cursor = 0; + + while let Some(&mut (module, ref mut processed)) = worklist.get_mut(cursor) { + cursor += 1; + if !visited_modules.insert((item, module)) { + // already processed this module + continue; } - let ext_def_map; - let data = if module.krate == from.krate { - if module.block.is_some() { - // Re-query the block's DefMap - ext_def_map = module.def_map(db); - &ext_def_map[module.local_id] - } else { - // Reuse the root DefMap - &def_map[module.local_id] - } + *processed = true; + let data = if module.block.is_some() { + // Re-query the block's DefMap + block_def_map = module.def_map(db); + &block_def_map[module.local_id] } else { - // The crate might reexport a module defined in another crate. - ext_def_map = module.def_map(db); - &ext_def_map[module.local_id] + // Reuse the root DefMap + &def_map[module.local_id] }; if let Some((name, vis, declared)) = data.scope.name_of(item) { @@ -535,18 +628,30 @@ fn find_local_import_locations( // the item and we're a submodule of it, so can we. // Also this keeps the cached data smaller. if declared || is_pub_or_explicit { - cb(name, module); + cb(visited_modules, name, module); } } } // Descend into all modules visible from `from`. for (module, vis) in data.scope.modules_in_scope() { + if module.krate != from.krate { + // We don't need to look at modules from other crates as our item has to be in the + // current crate + continue; + } + if visited_modules.contains(&(item, module)) { + continue; + } + if vis.is_visible_from(db, from) { - worklist.push(module); + worklist.push((module, false)); } } } + worklist.into_iter().filter(|&(_, processed)| processed).for_each(|(module, _)| { + visited_modules.remove(&(item, module)); + }); } #[cfg(test)] @@ -1514,8 +1619,6 @@ fn main() { #[test] fn from_inside_module() { - // This worked correctly, but the test suite logic was broken. - cov_mark::check!(submodule_in_testdb); check_found_path( r#" mod baz { @@ -1540,6 +1643,35 @@ mod bar { ) } + #[test] + fn from_inside_module2() { + check_found_path( + r#" +mod qux { + pub mod baz { + pub struct Foo {} + } + + mod bar { + fn bar() { + $0; + } + } +} + + "#, + "crate::qux::baz::Foo", + expect![[r#" + Plain (imports ✔): super::baz::Foo + Plain (imports ✖): super::baz::Foo + ByCrate(imports ✔): crate::qux::baz::Foo + ByCrate(imports ✖): crate::qux::baz::Foo + BySelf (imports ✔): super::baz::Foo + BySelf (imports ✖): super::baz::Foo + "#]], + ) + } + #[test] fn from_inside_module_with_inner_items() { check_found_path( @@ -1915,4 +2047,34 @@ pub fn c() {} "#]], ); } + + #[test] + fn prefer_long_std_over_short_extern() { + check_found_path( + r#" +//- /lib.rs crate:main deps:futures_lite,std,core +$0 +//- /futures_lite.rs crate:futures_lite deps:std,core +pub use crate::future::Future; +pub mod future { + pub use core::future::Future; +} +//- /std.rs crate:std deps:core +pub use core::future; +//- /core.rs crate:core +pub mod future { + pub trait Future {} +} +"#, + "core::future::Future", + expect![[r#" + Plain (imports ✔): std::future::Future + Plain (imports ✖): std::future::Future + ByCrate(imports ✔): std::future::Future + ByCrate(imports ✖): std::future::Future + BySelf (imports ✔): std::future::Future + BySelf (imports ✖): std::future::Future + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index d306f9be657a1..86fd092603ffd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -18,10 +18,9 @@ pub mod type_ref; use std::fmt; use hir_expand::name::Name; -use intern::Interned; +use intern::{Interned, Symbol}; use la_arena::{Idx, RawIdx}; use rustc_apfloat::ieee::{Half as f16, Quad as f128}; -use smallvec::SmallVec; use syntax::ast; use crate::{ @@ -60,41 +59,41 @@ pub type LabelId = Idx return None, + Some(_) => make::name(field_name), + None => return None, + }; let (offset, record_field) = record_field_layout( visibility, @@ -173,13 +177,18 @@ fn add_field_to_struct_fix( group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), - trigger_signature_help: false, + command: None, }) } 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 field_name = match field_name.chars().next() { + // FIXME : See match arm below regarding tuple structs. + Some(ch) if ch.is_numeric() => return None, + Some(_) => make::name(field_name), + None => return None, + }; let visibility = if error_range.file_id == struct_range.file_id { None } else { @@ -204,7 +213,7 @@ fn add_field_to_struct_fix( group: None, target: error_range.range, source_change: Some(src_change_builder.finish()), - trigger_signature_help: false, + command: None, }) } Some(FieldList::TupleFieldList(_tuple)) => { @@ -266,7 +275,7 @@ fn method_fix( file_id, TextEdit::insert(range.end(), "()".to_owned()), )), - trigger_signature_help: false, + command: None, }) } #[cfg(test)] @@ -275,7 +284,7 @@ mod tests { use crate::{ tests::{ check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled, - check_fix, + check_fix, check_no_fix, }, DiagnosticsConfig, }; @@ -460,4 +469,36 @@ fn foo() { "#, ); } + + #[test] + fn no_fix_when_indexed() { + check_no_fix( + r#" + struct Kek {} +impl Kek { + pub fn foo(self) { + self.$00 + } +} + +fn main() {} + "#, + ) + } + + #[test] + fn no_fix_when_without_field() { + check_no_fix( + r#" + struct Kek {} +impl Kek { + pub fn foo(self) { + self.$0 + } +} + +fn main() {} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 42211cdbe5d46..387d56b890b94 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -1,13 +1,12 @@ -use hir::{db::ExpandDatabase, AssocItem, HirDisplay, InFile}; +use hir::{db::ExpandDatabase, AssocItem, FileRange, HirDisplay, InFile}; use ide_db::{ assists::{Assist, AssistId, AssistKind}, - base_db::FileRange, label::Label, source_change::SourceChange, }; use syntax::{ ast::{self, make, HasArgList}, - AstNode, SmolStr, TextRange, + format_smolstr, AstNode, SmolStr, TextRange, ToSmolStr, }; use text_edit::TextEdit; @@ -105,10 +104,10 @@ fn field_fix( group: None, target: range, source_change: Some(SourceChange::from_iter([ - (file_id, TextEdit::insert(range.start(), "(".to_owned())), - (file_id, TextEdit::insert(range.end(), ")".to_owned())), + (file_id.into(), TextEdit::insert(range.start(), "(".to_owned())), + (file_id.into(), TextEdit::insert(range.end(), ")".to_owned())), ])), - trigger_signature_help: false, + command: None, }) } @@ -154,14 +153,16 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) - _ => false, }; - let mut receiver_type_adt_name = receiver_type.as_adt()?.name(db).to_smol_str().to_string(); + let mut receiver_type_adt_name = + receiver_type.as_adt()?.name(db).display_no_db().to_smolstr(); let generic_parameters: Vec = receiver_type.generic_parameters(db).collect(); // if receiver should be pass as first arg in the assoc func, // we could omit generic parameters cause compiler can deduce it automatically if !need_to_take_receiver_as_first_arg && !generic_parameters.is_empty() { let generic_parameters = generic_parameters.join(", "); - receiver_type_adt_name = format!("{receiver_type_adt_name}::<{generic_parameters}>"); + receiver_type_adt_name = + format_smolstr!("{receiver_type_adt_name}::<{generic_parameters}>"); } let method_name = call.name_ref()?; @@ -191,7 +192,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) - file_id, TextEdit::replace(range, assoc_func_call_expr_string), )), - trigger_signature_help: false, + command: None, }) } else { None diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 1155688324966..2bd8e484f8537 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -43,7 +43,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option, - d: &hir::UnresolvedProcMacro, - proc_macros_enabled: bool, - proc_attr_macros_enabled: bool, -) -> Diagnostic { - // Use more accurate position if available. - let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); - - let config_enabled = match d.kind { - hir::MacroKind::Attr => proc_macros_enabled && proc_attr_macros_enabled, - _ => proc_macros_enabled, - }; - - let not_expanded_message = match &d.macro_name { - Some(name) => format!("proc macro `{name}` not expanded"), - None => "proc macro not expanded".to_owned(), - }; - let severity = if config_enabled { Severity::Error } else { Severity::WeakWarning }; - let def_map = ctx.sema.db.crate_def_map(d.krate); - let message = if config_enabled { - def_map.proc_macro_loading_error().unwrap_or("internal error") - } else { - match d.kind { - hir::MacroKind::Attr if proc_macros_enabled => "attribute macro expansion is disabled", - _ => "proc-macro expansion is disabled", - } - }; - let message = format!("{not_expanded_message}: {message}"); - - Diagnostic::new(DiagnosticCode::Ra("unresolved-proc-macro", severity), message, display_range) -} 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 fdd4e862cafcd..bf19331d9fdd5 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,10 +1,9 @@ use hir::Name; use ide_db::{ assists::{Assist, AssistId, AssistKind}, - base_db::FileRange, label::Label, source_change::SourceChange, - RootDatabase, + FileRange, RootDatabase, }; use syntax::TextRange; use text_edit::TextEdit; @@ -43,7 +42,7 @@ pub(crate) fn unused_variables( ast, ) .with_fixes(name_range.and_then(|it| { - fixes(ctx.sema.db, var_name, it.range, diagnostic_range, ast.file_id.is_macro()) + fixes(ctx.sema.db, var_name, it.range, diagnostic_range.into(), ast.file_id.is_macro()) })) .experimental(), ) @@ -73,7 +72,7 @@ fn fixes( diagnostic_range.file_id, TextEdit::replace(name_range, format!("_{}", var_name.display(db))), )), - trigger_signature_help: false, + command: None, }]) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs index 79bcaa0a9c4c9..2d380ae045736 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,8 +1,5 @@ use hir::InFile; -use ide_db::{ - base_db::{FileId, FileRange}, - source_change::SourceChange, -}; +use ide_db::{source_change::SourceChange, EditionedFileId, FileRange}; use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; use text_edit::TextEdit; @@ -14,7 +11,7 @@ use crate::{fix, Diagnostic, DiagnosticCode}; // Diagnostic for unnecessary braces in `use` items. pub(crate) fn useless_braces( acc: &mut Vec, - file_id: FileId, + file_id: EditionedFileId, node: &SyntaxNode, ) -> Option<()> { let use_tree_list = ast::UseTreeList::cast(node.clone())?; @@ -41,7 +38,7 @@ pub(crate) fn useless_braces( Diagnostic::new( DiagnosticCode::RustcLint("unused_braces"), "Unnecessary braces in use statement".to_owned(), - FileRange { file_id, range: use_range }, + FileRange { file_id: file_id.into(), range: use_range }, ) .with_main_node(InFile::new(file_id.into(), SyntaxNodePtr::new(node))) .with_fixes(Some(vec![fix( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 6d1226d65c5cf..263ab74755999 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -62,7 +62,6 @@ mod handlers { pub(crate) mod unresolved_macro_call; pub(crate) mod unresolved_method; pub(crate) mod unresolved_module; - pub(crate) mod unresolved_proc_macro; pub(crate) mod unused_variables; // The handlers below are unusual, the implement the diagnostics as well. @@ -78,13 +77,13 @@ mod tests; use hir::{diagnostics::AnyDiagnostic, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, - base_db::{FileId, FileRange, SourceDatabase}, + base_db::SourceDatabase, generated::lints::{LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, source_change::SourceChange, syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, - FxHashMap, FxHashSet, RootDatabase, SnippetCap, + EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, SnippetCap, }; use once_cell::sync::Lazy; use stdx::never; @@ -144,12 +143,16 @@ pub struct Diagnostic { } impl Diagnostic { - fn new(code: DiagnosticCode, message: impl Into, range: FileRange) -> Diagnostic { + fn new( + code: DiagnosticCode, + message: impl Into, + range: impl Into, + ) -> Diagnostic { let message = message.into(); Diagnostic { code, message, - range, + range: range.into(), severity: match code { DiagnosticCode::RustcHardError(_) => Severity::Error, // FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings. @@ -290,6 +293,7 @@ impl DiagnosticsContext<'_> { } })() .unwrap_or_else(|| sema.diagnostics_display_range(*node)) + .into() } } @@ -303,6 +307,9 @@ pub fn diagnostics( ) -> Vec { let _p = tracing::info_span!("diagnostics").entered(); let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); let mut res = Vec::new(); // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. @@ -310,7 +317,7 @@ pub fn diagnostics( Diagnostic::new( DiagnosticCode::RustcHardError("syntax-error"), format!("Syntax Error: {err}"), - FileRange { file_id, range: err.range() }, + FileRange { file_id: file_id.into(), range: err.range() }, ) })); let parse_errors = res.len(); @@ -336,7 +343,7 @@ pub fn diagnostics( // file, so we skip semantic diagnostics so we can show these faster. Some(m) if parse_errors < 16 => m.diagnostics(db, &mut diags, config.style_lints), Some(_) => (), - None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id), + None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id.file_id()), } for diag in diags { @@ -397,7 +404,6 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d), AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), - AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { Some(it) => it, None => continue, @@ -613,7 +619,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { group: None, target, source_change: None, - trigger_signature_help: false, + command: None, } } @@ -627,4 +633,5 @@ fn adjusted_display_range( diag_ptr .with_value(adj(node).unwrap_or_else(|| diag_ptr.value.text_range())) .original_node_file_range_rooted(ctx.sema.db) + .into() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index cd5e95cc1e377..e56fca1e50082 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -60,7 +60,7 @@ fn check_nth_fix_with_config( let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let diagnostic = - super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id) + super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id.into()) .pop() .expect("no diagnostics"); let fix = &diagnostic @@ -102,34 +102,37 @@ pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; - let fix = super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) - .into_iter() - .find(|d| { - d.fixes - .as_ref() - .and_then(|fixes| { - fixes.iter().find(|fix| { - if !fix.target.contains_inclusive(file_position.offset) { - return false; - } - let actual = { - let source_change = fix.source_change.as_ref().unwrap(); - let file_id = *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = db.file_text(file_id).to_string(); + let fix = + super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into()) + .into_iter() + .find(|d| { + d.fixes + .as_ref() + .and_then(|fixes| { + fixes.iter().find(|fix| { + if !fix.target.contains_inclusive(file_position.offset) { + return false; + } + let actual = { + let source_change = fix.source_change.as_ref().unwrap(); + let file_id = + *source_change.source_file_edits.keys().next().unwrap(); + let mut actual = db.file_text(file_id).to_string(); - for (edit, snippet_edit) in source_change.source_file_edits.values() { - edit.apply(&mut actual); - if let Some(snippet_edit) = snippet_edit { - snippet_edit.apply(&mut actual); + for (edit, snippet_edit) in source_change.source_file_edits.values() + { + edit.apply(&mut actual); + if let Some(snippet_edit) = snippet_edit { + snippet_edit.apply(&mut actual); + } } - } - actual - }; - after == actual + actual + }; + after == actual + }) }) - }) - .is_some() - }); + .is_some() + }); assert!(fix.is_some(), "no diagnostic with desired fix"); } @@ -141,35 +144,38 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; let mut n_fixes = 0; - let fix = super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) - .into_iter() - .find(|d| { - d.fixes - .as_ref() - .and_then(|fixes| { - n_fixes += fixes.len(); - fixes.iter().find(|fix| { - if !fix.target.contains_inclusive(file_position.offset) { - return false; - } - let actual = { - let source_change = fix.source_change.as_ref().unwrap(); - let file_id = *source_change.source_file_edits.keys().next().unwrap(); - let mut actual = db.file_text(file_id).to_string(); + let fix = + super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into()) + .into_iter() + .find(|d| { + d.fixes + .as_ref() + .and_then(|fixes| { + n_fixes += fixes.len(); + fixes.iter().find(|fix| { + if !fix.target.contains_inclusive(file_position.offset) { + return false; + } + let actual = { + let source_change = fix.source_change.as_ref().unwrap(); + let file_id = + *source_change.source_file_edits.keys().next().unwrap(); + let mut actual = db.file_text(file_id).to_string(); - for (edit, snippet_edit) in source_change.source_file_edits.values() { - edit.apply(&mut actual); - if let Some(snippet_edit) = snippet_edit { - snippet_edit.apply(&mut actual); + for (edit, snippet_edit) in source_change.source_file_edits.values() + { + edit.apply(&mut actual); + if let Some(snippet_edit) = snippet_edit { + snippet_edit.apply(&mut actual); + } } - } - actual - }; - after == actual + actual + }; + after == actual + }) }) - }) - .is_some() - }); + .is_some() + }); assert!(fix.is_some(), "no diagnostic with desired fix"); assert!(n_fixes == 1, "Too many fixes suggested"); } @@ -181,7 +187,7 @@ pub(crate) fn check_no_fix(ra_fixture: &str) { &db, &DiagnosticsConfig::test_sample(), &AssistResolveStrategy::All, - file_position.file_id, + file_position.file_id.into(), ) .pop() .unwrap(); @@ -209,8 +215,9 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur .iter() .copied() .flat_map(|file_id| { - super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id).into_iter().map( - |d| { + super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into()) + .into_iter() + .map(|d| { let mut annotation = String::new(); if let Some(fixes) = &d.fixes { assert!(!fixes.is_empty()); @@ -225,12 +232,12 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur annotation.push_str(": "); annotation.push_str(&d.message); (d.range, annotation) - }, - ) + }) }) .map(|(diagnostic, annotation)| (diagnostic.file_id, (diagnostic.range, annotation))) .into_group_map(); for file_id in files { + let file_id = file_id.into(); let line_index = db.line_index(file_id); let mut actual = annotations.remove(&file_id).unwrap_or_default(); @@ -268,6 +275,7 @@ fn test_disabled_diagnostics() { config.disabled.insert("E0583".into()); let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); + let file_id = file_id.into(); let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); assert!(diagnostics.is_empty()); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs index 5b6e016251b40..a14e69030e325 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs @@ -1,10 +1,7 @@ //! This module allows building an SSR MatchFinder by parsing the SSR rule //! from a comment. -use ide_db::{ - base_db::{FilePosition, FileRange, SourceDatabase}, - RootDatabase, -}; +use ide_db::{base_db::SourceDatabase, EditionedFileId, FilePosition, FileRange, RootDatabase}; use syntax::{ ast::{self, AstNode, AstToken}, TextRange, @@ -20,7 +17,7 @@ pub fn ssr_from_comment( frange: FileRange, ) -> Option<(MatchFinder<'_>, TextRange)> { let comment = { - let file = db.parse(frange.file_id); + let file = db.parse(EditionedFileId::current_edition(frange.file_id)); file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast) }?; let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap(); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index 407433ed19230..e62ef60433649 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -83,9 +83,8 @@ mod tests; pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Match}; use crate::{errors::bail, matching::MatchFailureReason}; -use hir::Semantics; -use ide_db::base_db::{FileId, FilePosition, FileRange}; -use nohash_hasher::IntMap; +use hir::{FileRange, Semantics}; +use ide_db::{EditionedFileId, FileId, FxHashMap, RootDatabase}; use resolving::ResolvedRule; use syntax::{ast, AstNode, SyntaxNode, TextRange}; use text_edit::TextEdit; @@ -116,21 +115,27 @@ pub struct MatchFinder<'db> { sema: Semantics<'db, ide_db::RootDatabase>, rules: Vec, resolution_scope: resolving::ResolutionScope<'db>, - restrict_ranges: Vec, + restrict_ranges: Vec, } impl<'db> MatchFinder<'db> { /// Constructs a new instance where names will be looked up as if they appeared at /// `lookup_context`. pub fn in_context( - db: &'db ide_db::RootDatabase, - lookup_context: FilePosition, - mut restrict_ranges: Vec, + db: &'db RootDatabase, + lookup_context: ide_db::FilePosition, + mut restrict_ranges: Vec, ) -> Result, SsrError> { restrict_ranges.retain(|range| !range.range.is_empty()); let sema = Semantics::new(db); - let resolution_scope = resolving::ResolutionScope::new(&sema, lookup_context) - .ok_or_else(|| SsrError("no resolution scope for file".into()))?; + let file_id = sema + .attach_first_edition(lookup_context.file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(lookup_context.file_id)); + let resolution_scope = resolving::ResolutionScope::new( + &sema, + hir::FilePosition { file_id, offset: lookup_context.offset }, + ) + .ok_or_else(|| SsrError("no resolution scope for file".into()))?; Ok(MatchFinder { sema, rules: Vec::new(), resolution_scope, restrict_ranges }) } @@ -143,7 +148,7 @@ impl<'db> MatchFinder<'db> { { MatchFinder::in_context( db, - FilePosition { file_id: first_file_id, offset: 0.into() }, + ide_db::FilePosition { file_id: first_file_id, offset: 0.into() }, vec![], ) } else { @@ -166,12 +171,12 @@ impl<'db> MatchFinder<'db> { } /// Finds matches for all added rules and returns edits for all found matches. - pub fn edits(&self) -> IntMap { + pub fn edits(&self) -> FxHashMap { use ide_db::base_db::SourceDatabaseExt; - let mut matches_by_file = IntMap::default(); + let mut matches_by_file = FxHashMap::default(); for m in self.matches().matches { matches_by_file - .entry(m.range.file_id) + .entry(m.range.file_id.file_id()) .or_insert_with(SsrMatches::default) .matches .push(m); @@ -218,11 +223,15 @@ impl<'db> MatchFinder<'db> { /// Finds all nodes in `file_id` whose text is exactly equal to `snippet` and attempts to match /// them, while recording reasons why they don't match. This API is useful for command /// line-based debugging where providing a range is difficult. - pub fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec { + pub fn debug_where_text_equal( + &self, + file_id: EditionedFileId, + snippet: &str, + ) -> Vec { use ide_db::base_db::SourceDatabaseExt; let file = self.sema.parse(file_id); let mut res = Vec::new(); - let file_text = self.sema.db.file_text(file_id); + let file_text = self.sema.db.file_text(file_id.into()); let mut remaining_text = &*file_text; let mut base = 0; let len = snippet.len() as u32; @@ -349,7 +358,7 @@ impl std::error::Error for SsrError {} #[cfg(test)] impl MatchDebugInfo { - pub(crate) fn match_failure_reason(&self) -> Option<&str> { + pub fn match_failure_reason(&self) -> Option<&str> { self.matched.as_ref().err().map(|r| r.reason.as_str()) } } 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 7c357b3c2174a..5f6d77c064ca3 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -6,8 +6,8 @@ use crate::{ resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo}, SsrMatches, }; -use hir::{ImportPathConfig, Semantics}; -use ide_db::{base_db::FileRange, FxHashMap}; +use hir::{FileRange, ImportPathConfig, Semantics}; +use ide_db::FxHashMap; use std::{cell::Cell, iter::Peekable}; use syntax::{ ast::{self, AstNode, AstToken, HasGenericArgs}, @@ -801,7 +801,12 @@ mod tests { let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }"; let (db, position, selections) = crate::tests::single_file(input); - let mut match_finder = MatchFinder::in_context(&db, position, selections).unwrap(); + let mut match_finder = MatchFinder::in_context( + &db, + position.into(), + selections.into_iter().map(Into::into).collect(), + ) + .unwrap(); match_finder.add_rule(rule).unwrap(); let matches = match_finder.matches(); assert_eq!(matches.matches.len(), 1); @@ -810,7 +815,7 @@ mod tests { let edits = match_finder.edits(); assert_eq!(edits.len(), 1); - let edit = &edits[&position.file_id]; + let edit = &edits[&position.file_id.into()]; let mut after = input.to_owned(); edit.apply(&mut after); assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }"); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/parsing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/parsing.rs index 2f91271c46535..e752ee3d775f2 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/parsing.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/parsing.rs @@ -255,7 +255,7 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { } fn tokenize(source: &str) -> Result, SsrError> { - let lexed = parser::LexedStr::new(source); + let lexed = parser::LexedStr::new(parser::Edition::CURRENT, source); if let Some((_, first_error)) = lexed.errors().next() { bail!("Failed to parse pattern: {}", first_error); } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs index d3c1af1f31e1f..270ee0b3ec967 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs @@ -1,7 +1,7 @@ //! This module is responsible for resolving paths within rules. use hir::AsAssocItem; -use ide_db::{base_db::FilePosition, FxHashMap}; +use ide_db::FxHashMap; use parsing::Placeholder; use syntax::{ ast::{self, HasGenericArgs}, @@ -195,7 +195,7 @@ impl Resolver<'_, '_> { impl<'db> ResolutionScope<'db> { pub(crate) fn new( sema: &hir::Semantics<'db, ide_db::RootDatabase>, - resolve_context: FilePosition, + resolve_context: hir::FilePosition, ) -> Option> { use syntax::ast::AstNode; let file = sema.parse(resolve_context.file_id); @@ -238,7 +238,7 @@ impl<'db> ResolutionScope<'db> { None, |assoc_item| { let item_name = assoc_item.name(self.scope.db)?; - if item_name.to_smol_str().as_str() == name.text() { + if item_name.as_str() == name.text() { Some(hir::PathResolution::Def(assoc_item.into())) } else { None 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 55a49da242404..832386685d72c 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs @@ -5,11 +5,11 @@ use crate::{ resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, Match, MatchFinder, }; +use hir::FileRange; use ide_db::{ - base_db::{FileId, FileRange}, defs::Definition, search::{SearchScope, UsageSearchResult}, - FxHashSet, + EditionedFileId, FileId, FxHashSet, }; use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; @@ -136,14 +136,18 @@ impl MatchFinder<'_> { // seems to get put into a single source root. let mut files = Vec::new(); self.search_files_do(|file_id| { - files.push(file_id); + files.push( + self.sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)), + ); }); SearchScope::files(&files) } fn slow_scan(&self, rule: &ResolvedRule, matches_out: &mut Vec) { self.search_files_do(|file_id| { - let file = self.sema.parse(file_id); + let file = self.sema.parse_guess_edition(file_id); let code = file.syntax(); self.slow_scan_node(code, rule, &None, matches_out); }) diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index e608b0a7c4268..4477a268b2912 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -1,7 +1,8 @@ use expect_test::{expect, Expect}; +use hir::{FilePosition, FileRange}; use ide_db::{ - base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}, - FxHashSet, + base_db::{salsa::Durability, SourceDatabaseExt}, + EditionedFileId, FxHashSet, }; use test_utils::RangeOrOffset; use triomphe::Arc; @@ -97,7 +98,12 @@ fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { let (db, position, selections) = single_file(input); - let mut match_finder = MatchFinder::in_context(&db, position, selections).unwrap(); + let mut match_finder = MatchFinder::in_context( + &db, + position.into(), + selections.into_iter().map(Into::into).collect(), + ) + .unwrap(); for rule in rules { let rule: SsrRule = rule.parse().unwrap(); match_finder.add_rule(rule).unwrap(); @@ -108,13 +114,13 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { } // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters // stuff. - let mut actual = db.file_text(position.file_id).to_string(); - edits[&position.file_id].apply(&mut actual); + let mut actual = db.file_text(position.file_id.into()).to_string(); + edits[&position.file_id.into()].apply(&mut actual); expected.assert_eq(&actual); } #[allow(clippy::print_stdout)] -fn print_match_debug_info(match_finder: &MatchFinder<'_>, file_id: FileId, snippet: &str) { +fn print_match_debug_info(match_finder: &MatchFinder<'_>, file_id: EditionedFileId, snippet: &str) { let debug_info = match_finder.debug_where_text_equal(file_id, snippet); println!( "Match debug info: {} nodes had text exactly equal to '{}'", @@ -128,7 +134,12 @@ fn print_match_debug_info(match_finder: &MatchFinder<'_>, file_id: FileId, snipp fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { let (db, position, selections) = single_file(code); - let mut match_finder = MatchFinder::in_context(&db, position, selections).unwrap(); + let mut match_finder = MatchFinder::in_context( + &db, + position.into(), + selections.into_iter().map(Into::into).collect(), + ) + .unwrap(); match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); let matched_strings: Vec = match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); @@ -140,7 +151,12 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { fn assert_no_match(pattern: &str, code: &str) { let (db, position, selections) = single_file(code); - let mut match_finder = MatchFinder::in_context(&db, position, selections).unwrap(); + let mut match_finder = MatchFinder::in_context( + &db, + position.into(), + selections.into_iter().map(Into::into).collect(), + ) + .unwrap(); match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); let matches = match_finder.matches().flattened().matches; if !matches.is_empty() { @@ -151,7 +167,12 @@ fn assert_no_match(pattern: &str, code: &str) { fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { let (db, position, selections) = single_file(code); - let mut match_finder = MatchFinder::in_context(&db, position, selections).unwrap(); + let mut match_finder = MatchFinder::in_context( + &db, + position.into(), + selections.into_iter().map(Into::into).collect(), + ) + .unwrap(); match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); let mut reasons = Vec::new(); for d in match_finder.debug_where_text_equal(position.file_id, snippet) { @@ -452,7 +473,7 @@ fn match_struct_instantiation() { fn match_path() { let code = r#" mod foo { - pub fn bar() {} + pub(crate) fn bar() {} } fn f() {foo::bar(42)}"#; assert_matches("foo::bar", code, &["foo::bar"]); @@ -471,8 +492,8 @@ fn match_pattern() { fn match_fully_qualified_fn_path() { let code = r#" mod a { - pub mod b { - pub fn c(_: i32) {} + pub(crate) mod b { + pub(crate) fn c(_: i32) {} } } use a::b::c; @@ -487,8 +508,8 @@ fn match_fully_qualified_fn_path() { fn match_resolved_type_name() { let code = r#" mod m1 { - pub mod m2 { - pub trait Foo {} + pub(crate) mod m2 { + pub(crate) trait Foo {} } } mod m3 { @@ -508,9 +529,9 @@ fn type_arguments_within_path() { cov_mark::check!(type_arguments_within_path); let code = r#" mod foo { - pub struct Bar {t: T} + pub(crate) struct Bar {t: T} impl Bar { - pub fn baz() {} + pub(crate) fn baz() {} } } fn f1() {foo::Bar::::baz();} @@ -659,9 +680,9 @@ fn replace_associated_trait_default_function_call() { "Bar2::foo() ==>> Bar2::foo2()", r#" trait Foo { fn foo() {} } - pub struct Bar {} + pub(crate) struct Bar {} impl Foo for Bar {} - pub struct Bar2 {} + pub(crate) struct Bar2 {} impl Foo for Bar2 {} impl Bar2 { fn foo2() {} } fn main() { @@ -671,9 +692,9 @@ fn replace_associated_trait_default_function_call() { "#, expect![[r#" trait Foo { fn foo() {} } - pub struct Bar {} + pub(crate) struct Bar {} impl Foo for Bar {} - pub struct Bar2 {} + pub(crate) struct Bar2 {} impl Foo for Bar2 {} impl Bar2 { fn foo2() {} } fn main() { @@ -691,9 +712,9 @@ fn replace_associated_trait_constant() { "Bar2::VALUE ==>> Bar2::VALUE_2222", r#" trait Foo { const VALUE: i32; const VALUE_2222: i32; } - pub struct Bar {} + pub(crate) struct Bar {} impl Foo for Bar { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } - pub struct Bar2 {} + pub(crate) struct Bar2 {} impl Foo for Bar2 { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } impl Bar2 { fn foo2() {} } fn main() { @@ -703,9 +724,9 @@ fn replace_associated_trait_constant() { "#, expect![[r#" trait Foo { const VALUE: i32; const VALUE_2222: i32; } - pub struct Bar {} + pub(crate) struct Bar {} impl Foo for Bar { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } - pub struct Bar2 {} + pub(crate) struct Bar2 {} impl Foo for Bar2 { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } impl Bar2 { fn foo2() {} } fn main() { @@ -726,10 +747,10 @@ fn replace_path_in_different_contexts() { "c::foo() ==>> c::bar()", r#" mod a { - pub mod b {$0 - pub mod c { - pub fn foo() {} - pub fn bar() {} + pub(crate) mod b {$0 + pub(crate) mod c { + pub(crate) fn foo() {} + pub(crate) fn bar() {} fn f1() { foo() } } fn f2() { c::foo() } @@ -741,10 +762,10 @@ fn replace_path_in_different_contexts() { "#, expect![[r#" mod a { - pub mod b { - pub mod c { - pub fn foo() {} - pub fn bar() {} + pub(crate) mod b { + pub(crate) mod c { + pub(crate) fn foo() {} + pub(crate) fn bar() {} fn f1() { bar() } } fn f2() { c::bar() } @@ -763,15 +784,15 @@ fn replace_associated_function_with_generics() { "c::Foo::<$a>::new() ==>> d::Bar::<$a>::default()", r#" mod c { - pub struct Foo {v: T} - impl Foo { pub fn new() {} } + pub(crate) struct Foo {v: T} + impl Foo { pub(crate) fn new() {} } fn f1() { Foo::::new(); } } mod d { - pub struct Bar {v: T} - impl Bar { pub fn default() {} } + pub(crate) struct Bar {v: T} + impl Bar { pub(crate) fn default() {} } fn f1() { super::c::Foo::::new(); } @@ -779,15 +800,15 @@ fn replace_associated_function_with_generics() { "#, expect![[r#" mod c { - pub struct Foo {v: T} - impl Foo { pub fn new() {} } + pub(crate) struct Foo {v: T} + impl Foo { pub(crate) fn new() {} } fn f1() { crate::d::Bar::::default(); } } mod d { - pub struct Bar {v: T} - impl Bar { pub fn default() {} } + pub(crate) struct Bar {v: T} + impl Bar { pub(crate) fn default() {} } fn f1() { Bar::::default(); } @@ -823,9 +844,9 @@ fn f1() -> DynTrait> {foo()} #[test] fn replace_macro_invocations() { assert_ssr_transform( - "try!($a) ==>> $a?", - "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}", - expect![["macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}"]], + "try_!($a) ==>> $a?", + "macro_rules! try_ {() => {}} fn f1() -> Result<(), E> {bar(try_!(foo()));}", + expect![["macro_rules! try_ {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}"]], ); // FIXME: Figure out why this doesn't work anymore // assert_ssr_transform( @@ -1029,14 +1050,14 @@ fn use_declaration_with_braces() { assert_ssr_transform( "foo::bar ==>> foo2::bar2", r#" - mod foo { pub fn bar() {} pub fn baz() {} } - mod foo2 { pub fn bar2() {} } + mod foo { pub(crate) fn bar() {} pub(crate) fn baz() {} } + mod foo2 { pub(crate) fn bar2() {} } use foo::{baz, bar}; fn main() { bar() } "#, expect![[" - mod foo { pub fn bar() {} pub fn baz() {} } - mod foo2 { pub fn bar2() {} } + mod foo { pub(crate) fn bar() {} pub(crate) fn baz() {} } + mod foo2 { pub(crate) fn bar2() {} } use foo::{baz, bar}; fn main() { foo2::bar2() } "]], @@ -1266,9 +1287,9 @@ fn match_trait_method_call() { // `Bar::foo` and `Bar2::foo` resolve to the same function. Make sure we only match if the type // matches what's in the pattern. Also checks that we handle autoderef. let code = r#" - pub struct Bar {} - pub struct Bar2 {} - pub trait Foo { + pub(crate) struct Bar {} + pub(crate) struct Bar2 {} + pub(crate) trait Foo { fn foo(&self, _: i32) {} } impl Foo for Bar {} diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index f49c5af0af1f1..8e0166a4a76ca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -1,9 +1,7 @@ use hir::{HasSource, InFile, InRealFile, Semantics}; use ide_db::{ - base_db::{FileId, FilePosition, FileRange}, - defs::Definition, - helpers::visit_file_defs, - FxHashSet, RootDatabase, + defs::Definition, helpers::visit_file_defs, FileId, FilePosition, FileRange, FxHashSet, + RootDatabase, }; use itertools::Itertools; use syntax::{ast::HasName, AstNode, TextRange}; @@ -256,7 +254,7 @@ fn main() { Annotation { range: 6..10, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -264,7 +262,7 @@ fn main() { }, data: Some( [ - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -277,7 +275,7 @@ fn main() { Annotation { range: 30..36, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -310,7 +308,7 @@ fn main() { Annotation { range: 53..57, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -341,7 +339,7 @@ fn main() { Annotation { range: 7..11, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -349,7 +347,7 @@ fn main() { }, data: Some( [ - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -362,7 +360,7 @@ fn main() { Annotation { range: 7..11, kind: HasImpls { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -395,7 +393,7 @@ fn main() { Annotation { range: 17..21, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -430,7 +428,7 @@ fn main() { Annotation { range: 7..11, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -438,13 +436,13 @@ fn main() { }, data: Some( [ - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), range: 57..61, }, - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -457,7 +455,7 @@ fn main() { Annotation { range: 7..11, kind: HasImpls { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -481,7 +479,7 @@ fn main() { Annotation { range: 20..31, kind: HasImpls { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -505,7 +503,7 @@ fn main() { Annotation { range: 20..31, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -513,7 +511,7 @@ fn main() { }, data: Some( [ - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -526,7 +524,7 @@ fn main() { Annotation { range: 69..73, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -591,7 +589,7 @@ fn main() {} Annotation { range: 3..7, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -626,7 +624,7 @@ fn main() { Annotation { range: 7..11, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -634,13 +632,13 @@ fn main() { }, data: Some( [ - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), range: 19..23, }, - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -653,7 +651,7 @@ fn main() { Annotation { range: 7..11, kind: HasImpls { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -677,7 +675,7 @@ fn main() { Annotation { range: 33..44, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -685,7 +683,7 @@ fn main() { }, data: Some( [ - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -698,7 +696,7 @@ fn main() { Annotation { range: 61..65, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -749,7 +747,7 @@ mod tests { Annotation { range: 3..7, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -879,7 +877,7 @@ struct Foo; Annotation { range: 0..71, kind: HasReferences { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), @@ -893,7 +891,7 @@ struct Foo; Annotation { range: 0..71, kind: HasImpls { - pos: FilePosition { + pos: FilePositionWrapper { file_id: FileId( 0, ), diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs index 2e7e230e5acaa..08cc10509cb8a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs @@ -13,7 +13,7 @@ pub(super) fn find_all_methods( file_id: FileId, ) -> Vec<(TextRange, Option)> { let sema = Semantics::new(db); - let source_file = sema.parse(file_id); + let source_file = sema.parse_guess_edition(file_id); source_file.syntax().descendants().filter_map(method_range).collect() } diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 3c29f2f4276bc..87093104852f3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -7,9 +7,8 @@ use ide_db::{ defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, search::FileReference, - FxIndexMap, RootDatabase, + FileRange, FxIndexMap, RootDatabase, }; -use span::FileRange; use syntax::{ast, AstNode, SyntaxKind::IDENT}; use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo, TryToNav}; @@ -33,7 +32,7 @@ pub(crate) fn incoming_calls( ) -> Option> { let sema = &Semantics::new(db); - let file = sema.parse(file_id); + let file = sema.parse_guess_edition(file_id); let file = file.syntax(); let mut calls = CallLocations::default(); @@ -63,9 +62,9 @@ pub(crate) fn incoming_calls( }); if let Some(nav) = nav { let range = sema.original_range(name.syntax()); - calls.add(nav.call_site, range); + calls.add(nav.call_site, range.into()); if let Some(other) = nav.def_site { - calls.add(other, range); + calls.add(other, range.into()); } } } @@ -79,7 +78,7 @@ pub(crate) fn outgoing_calls( FilePosition { file_id, offset }: FilePosition, ) -> Option> { let sema = Semantics::new(db); - let file = sema.parse(file_id); + let file = sema.parse_guess_edition(file_id); let file = file.syntax(); let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT => 1, @@ -121,7 +120,7 @@ pub(crate) fn outgoing_calls( Some(nav_target.into_iter().zip(iter::repeat(range))) }) .flatten() - .for_each(|(nav, range)| calls.add(nav, range)); + .for_each(|(nav, range)| calls.add(nav, range.into())); Some(calls.into_items()) } @@ -144,7 +143,7 @@ impl CallLocations { #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ide_db::base_db::FilePosition; + use ide_db::FilePosition; use itertools::Itertools; use crate::fixture; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index f42613637b257..e9e5240897e17 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -11,7 +11,8 @@ use stdx::format_to; use url::Url; use hir::{ - db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, DescendPreference, HasAttrs, + db::HirDatabase, sym, Adt, AsAssocItem, AssocItem, AssocItemContainer, DescendPreference, + HasAttrs, }; use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, @@ -136,7 +137,7 @@ pub(crate) fn external_docs( sysroot: Option<&str>, ) -> Option { let sema = &Semantics::new(db); - let file = sema.parse(file_id).syntax().clone(); + let file = sema.parse_guess_edition(file_id).syntax().clone(); let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 3, T!['('] | T![')'] => 2, @@ -593,12 +594,14 @@ fn filename_and_frag_for_def( }, Definition::Module(m) => match m.name(db) { // `#[doc(keyword = "...")]` is internal used only by rust compiler - Some(name) => match m.attrs(db).by_key("doc").find_string_value_in_tt("keyword") { - Some(kw) => { - format!("keyword.{}.html", kw.trim_matches('"')) + Some(name) => { + match m.attrs(db).by_key(&sym::doc).find_string_value_in_tt(&sym::keyword) { + Some(kw) => { + format!("keyword.{}.html", kw) + } + None => format!("{}/index.html", name.display(db.upcast())), } - None => format!("{}/index.html", name.display(db.upcast())), - }, + } None => String::from("index.html"), }, Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())), diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index ac44908a90214..fe91c81a61513 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -3,10 +3,9 @@ use std::iter; use expect_test::{expect, Expect}; use hir::Semantics; use ide_db::{ - base_db::{FilePosition, FileRange}, defs::Definition, documentation::{Documentation, HasDocs}, - RootDatabase, + FilePosition, FileRange, RootDatabase, }; use itertools::Itertools; use syntax::{ast, match_ast, AstNode, SyntaxNode}; @@ -80,7 +79,7 @@ fn def_under_cursor( position: &FilePosition, ) -> (Definition, Documentation) { let (docs, def) = sema - .parse(position.file_id) + .parse_guess_edition(position.file_id) .syntax() .token_at_offset(position.offset) .left_biased() diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 4b54c057bf335..c8fe45c9cf0f8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,7 +1,7 @@ use hir::{DescendPreference, InFile, MacroFileIdExt, Semantics}; use ide_db::{ - base_db::FileId, helpers::pick_best_token, - syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, + helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, FileId, + RootDatabase, }; use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T}; @@ -25,7 +25,7 @@ pub struct ExpandedMacro { // image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); - let file = sema.parse(position.file_id); + let file = sema.parse_guess_edition(position.file_id); let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 1, diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index e8d6dc97341c7..5f6aaeaabb60e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -26,7 +26,7 @@ use crate::FileRange; // image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[] pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { let sema = Semantics::new(db); - let src = sema.parse(frange.file_id); + let src = sema.parse_guess_edition(frange.file_id); try_extend_selection(&sema, src.syntax(), frange).unwrap_or(frange.range) } diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs index 14c2655f84d24..37b3cb03b3331 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs @@ -1,6 +1,6 @@ use ide_db::{ - base_db::{CrateOrigin, FileId, SourceDatabase}, - FxIndexSet, RootDatabase, + base_db::{CrateOrigin, SourceDatabase}, + FileId, FxIndexSet, RootDatabase, }; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -38,5 +38,5 @@ fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo { } fn crate_name(data: &ide_db::base_db::CrateData) -> Option { - data.display_name.as_ref().map(|it| it.canonical_name().to_owned()) + data.display_name.as_ref().map(|it| it.canonical_name().as_str().to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 568906a098e34..92458185849b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -197,7 +197,9 @@ fn structure_token(token: SyntaxToken) -> Option { if let Some(comment) = ast::Comment::cast(token) { let text = comment.text().trim(); - if let Some(region_name) = text.strip_prefix("// region:").map(str::trim) { + if let Some(region_name) = + text.strip_prefix("// region:").map(str::trim).filter(|it| !it.is_empty()) + { return Some(StructureNode { parent: None, label: region_name.to_owned(), diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs index 3b19b85c4bc12..b16511072bd0c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs @@ -10,7 +10,7 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) { let change_fixture = ChangeFixture::parse(ra_fixture); host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); - (host.analysis(), change_fixture.files[0]) + (host.analysis(), change_fixture.files[0].into()) } /// Creates analysis from a multi-file fixture, returns positions marked with $0. @@ -21,7 +21,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - (host.analysis(), FilePosition { file_id, offset }) + (host.analysis(), FilePosition { file_id: file_id.into(), offset }) } /// Creates analysis for a single file, returns range marked with a pair of $0. @@ -32,7 +32,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let range = range_or_offset.expect_range(); - (host.analysis(), FileRange { file_id, range }) + (host.analysis(), FileRange { file_id: file_id.into(), range }) } /// Creates analysis for a single file, returns range marked with a pair of $0 or a position marked with $0. @@ -42,7 +42,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); - (host.analysis(), file_id, range_or_offset) + (host.analysis(), file_id.into(), range_or_offset) } /// Creates analysis from a multi-file fixture, returns positions marked with $0. @@ -58,12 +58,14 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil .files .iter() .flat_map(|&file_id| { - let file_text = host.analysis().file_text(file_id).unwrap(); + let file_text = host.analysis().file_text(file_id.into()).unwrap(); let annotations = extract_annotations(&file_text); - annotations.into_iter().map(move |(range, data)| (FileRange { file_id, range }, data)) + annotations + .into_iter() + .map(move |(range, data)| (FileRange { file_id: file_id.into(), range }, data)) }) .collect(); - (host.analysis(), FilePosition { file_id, offset }, annotations) + (host.analysis(), FilePosition { file_id: file_id.into(), offset }, annotations) } /// Creates analysis from a multi-file fixture with annotations without $0 @@ -77,9 +79,11 @@ pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(Fi .files .iter() .flat_map(|&file_id| { - let file_text = host.analysis().file_text(file_id).unwrap(); + let file_text = host.analysis().file_text(file_id.into()).unwrap(); let annotations = extract_annotations(&file_text); - annotations.into_iter().map(move |(range, data)| (FileRange { file_id, range }, data)) + annotations + .into_iter() + .map(move |(range, data)| (FileRange { file_id: file_id.into(), range }, data)) }) .collect(); (host.analysis(), annotations) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index c19f19803f17a..6076de54ebaf8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -23,7 +23,7 @@ pub(crate) fn goto_declaration( position @ FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = Semantics::new(db); - let file = sema.parse(file_id).syntax().clone(); + let file = sema.parse_guess_edition(file_id).syntax().clone(); let original_token = file .token_at_offset(offset) .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?; @@ -78,7 +78,7 @@ pub(crate) fn goto_declaration( #[cfg(test)] mod tests { - use ide_db::base_db::FileRange; + use ide_db::FileRange; use itertools::Itertools; use crate::fixture; 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 f57cb1cb73025..d0701a45b1019 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1,18 +1,29 @@ use std::{iter, mem::discriminant}; use crate::{ - doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget, - RangeInfo, TryToNav, + doc_links::token_as_doc_comment, + navigation_target::{self, ToNav}, + FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, +}; +use hir::{ + AsAssocItem, AssocItem, DescendPreference, FileRange, InFile, MacroFileIdExt, ModuleDef, + Semantics, }; -use hir::{AsAssocItem, AssocItem, DescendPreference, MacroFileIdExt, ModuleDef, Semantics}; use ide_db::{ - base_db::{AnchoredPath, FileId, FileLoader}, + base_db::{AnchoredPath, FileLoader}, defs::{Definition, IdentClass}, helpers::pick_best_token, - RootDatabase, + RootDatabase, SymbolKind, }; use itertools::Itertools; -use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; + +use span::FileId; +use syntax::{ + ast::{self, HasLoopBody}, + match_ast, AstNode, AstToken, + SyntaxKind::*, + SyntaxNode, SyntaxToken, TextRange, T, +}; // Feature: Go to Definition // @@ -32,7 +43,7 @@ pub(crate) fn goto_definition( FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = &Semantics::new(db); - let file = sema.parse(file_id).syntax().clone(); + let file = sema.parse_guess_edition(file_id).syntax().clone(); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER @@ -68,6 +79,10 @@ pub(crate) fn goto_definition( )); } + if let Some(navs) = handle_control_flow_keywords(sema, &original_token) { + return Some(RangeInfo::new(original_token.text_range(), navs)); + } + let navs = sema .descend_into_macros(DescendPreference::None, original_token.clone()) .into_iter() @@ -150,7 +165,7 @@ fn try_lookup_macro_def_in_macro_use( for mod_def in krate.root_module().declarations(sema.db) { if let ModuleDef::Macro(mac) = mod_def { - if mac.name(sema.db).as_str() == Some(token.text()) { + if mac.name(sema.db).as_str() == token.text() { if let Some(nav) = mac.try_to_nav(sema.db) { return Some(nav.call_site); } @@ -190,13 +205,224 @@ fn try_filter_trait_item_definition( } } +fn handle_control_flow_keywords( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Option> { + match token.kind() { + // For `fn` / `loop` / `while` / `for` / `async`, return the keyword it self, + // so that VSCode will find the references when using `ctrl + click` + T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token), + T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token), + T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => { + nav_for_break_points(sema, token) + } + _ => None, + } +} + +pub(crate) fn find_fn_or_blocks( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Vec { + let find_ancestors = |token: SyntaxToken| { + let token_kind = token.kind(); + + for anc in sema.token_ancestors_with_macros(token) { + let node = match_ast! { + match anc { + ast::Fn(fn_) => fn_.syntax().clone(), + ast::ClosureExpr(c) => c.syntax().clone(), + ast::BlockExpr(blk) => { + match blk.modifier() { + Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(), + Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => blk.syntax().clone(), + _ => continue, + } + }, + _ => continue, + } + }; + + return Some(node); + } + None + }; + + sema.descend_into_macros(DescendPreference::None, token.clone()) + .into_iter() + .filter_map(find_ancestors) + .collect_vec() +} + +fn nav_for_exit_points( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Option> { + let db = sema.db; + let token_kind = token.kind(); + + let navs = find_fn_or_blocks(sema, token) + .into_iter() + .filter_map(|node| { + let file_id = sema.hir_file_for(&node); + + match_ast! { + match node { + ast::Fn(fn_) => { + let mut nav = sema.to_def(&fn_)?.try_to_nav(db)?; + // For async token, we navigate to itself, which triggers + // VSCode to find the references + let focus_token = if matches!(token_kind, T![async]) { + fn_.async_token()? + } else { + fn_.fn_token()? + }; + + let focus_frange = InFile::new(file_id, focus_token.text_range()) + .original_node_file_range_opt(db) + .map(|(frange, _)| frange); + + if let Some(FileRange { file_id, range }) = focus_frange { + let contains_frange = |nav: &NavigationTarget| { + nav.file_id == file_id && nav.full_range.contains_range(range) + }; + + if let Some(def_site) = nav.def_site.as_mut() { + if contains_frange(def_site) { + def_site.focus_range = Some(range); + } + } else if contains_frange(&nav.call_site) { + nav.call_site.focus_range = Some(range); + } + } + + Some(nav) + }, + ast::ClosureExpr(c) => { + let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.text_range(); + let closure_in_file = InFile::new(file_id, c.into()); + Some(expr_to_nav(db, closure_in_file, Some(pipe_tok))) + }, + ast::BlockExpr(blk) => { + match blk.modifier() { + Some(ast::BlockModifier::Async(_)) => { + let async_tok = blk.async_token()?.text_range(); + let blk_in_file = InFile::new(file_id, blk.into()); + Some(expr_to_nav(db, blk_in_file, Some(async_tok))) + }, + Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => { + let try_tok = blk.try_token()?.text_range(); + let blk_in_file = InFile::new(file_id, blk.into()); + Some(expr_to_nav(db, blk_in_file, Some(try_tok))) + }, + _ => None, + } + }, + _ => None, + } + } + }) + .flatten() + .collect_vec(); + + Some(navs) +} + +pub(crate) fn find_loops( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Option> { + let parent = token.parent()?; + let lbl = match_ast! { + match parent { + ast::BreakExpr(break_) => break_.lifetime(), + ast::ContinueExpr(continue_) => continue_.lifetime(), + _ => None, + } + }; + let label_matches = + |it: Option| match (lbl.as_ref(), it.and_then(|it| it.lifetime())) { + (Some(lbl), Some(it)) => lbl.text() == it.text(), + (None, _) => true, + (Some(_), None) => false, + }; + + let find_ancestors = |token: SyntaxToken| { + for anc in sema.token_ancestors_with_macros(token).filter_map(ast::Expr::cast) { + let node = match &anc { + ast::Expr::LoopExpr(loop_) if label_matches(loop_.label()) => anc, + ast::Expr::WhileExpr(while_) if label_matches(while_.label()) => anc, + ast::Expr::ForExpr(for_) if label_matches(for_.label()) => anc, + ast::Expr::BlockExpr(blk) + if blk.label().is_some() && label_matches(blk.label()) => + { + anc + } + _ => continue, + }; + + return Some(node); + } + None + }; + + sema.descend_into_macros(DescendPreference::None, token.clone()) + .into_iter() + .filter_map(find_ancestors) + .collect_vec() + .into() +} + +fn nav_for_break_points( + sema: &Semantics<'_, RootDatabase>, + token: &SyntaxToken, +) -> Option> { + let db = sema.db; + + let navs = find_loops(sema, token)? + .into_iter() + .filter_map(|expr| { + let file_id = sema.hir_file_for(expr.syntax()); + let expr_in_file = InFile::new(file_id, expr.clone()); + let focus_range = match expr { + ast::Expr::LoopExpr(loop_) => loop_.loop_token()?.text_range(), + ast::Expr::WhileExpr(while_) => while_.while_token()?.text_range(), + ast::Expr::ForExpr(for_) => for_.for_token()?.text_range(), + // We guarantee that the label exists + ast::Expr::BlockExpr(blk) => blk.label().unwrap().syntax().text_range(), + _ => return None, + }; + let nav = expr_to_nav(db, expr_in_file, Some(focus_range)); + Some(nav) + }) + .flatten() + .collect_vec(); + + Some(navs) +} + fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec { def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default() } +fn expr_to_nav( + db: &RootDatabase, + InFile { file_id, value }: InFile, + focus_range: Option, +) -> UpmappingResult { + let kind = SymbolKind::Label; + + let value_range = value.syntax().text_range(); + let navs = navigation_target::orig_range_with_focus_r(db, file_id, value_range, focus_range); + navs.map(|(hir::FileRangeWrapper { file_id, range }, focus_range)| { + NavigationTarget::from_syntax(file_id, "".into(), focus_range, range, kind) + }) +} + #[cfg(test)] mod tests { - use ide_db::base_db::FileRange; + use ide_db::FileRange; use itertools::Itertools; use crate::fixture; @@ -2313,4 +2539,200 @@ pub mod prelude { "#, ); } + + #[test] + fn goto_def_on_return_kw() { + check( + r#" +macro_rules! N { + ($i:ident, $x:expr, $blk:expr) => { + for $i in 0..$x { + $blk + } + }; +} + +fn main() { + fn f() { + // ^^ + N!(i, 5, { + println!("{}", i); + return$0; + }); + + for i in 1..5 { + return; + } + (|| { + return; + })(); + } +} +"#, + ) + } + + #[test] + fn goto_def_on_return_kw_in_closure() { + check( + r#" +macro_rules! N { + ($i:ident, $x:expr, $blk:expr) => { + for $i in 0..$x { + $blk + } + }; +} + +fn main() { + fn f() { + N!(i, 5, { + println!("{}", i); + return; + }); + + for i in 1..5 { + return; + } + (|| { + // ^ + return$0; + })(); + } +} +"#, + ) + } + + #[test] + fn goto_def_on_break_kw() { + check( + r#" +fn main() { + for i in 1..5 { + // ^^^ + break$0; + } +} +"#, + ) + } + + #[test] + fn goto_def_on_continue_kw() { + check( + r#" +fn main() { + for i in 1..5 { + // ^^^ + continue$0; + } +} +"#, + ) + } + + #[test] + fn goto_def_on_break_kw_for_block() { + check( + r#" +fn main() { + 'a:{ + // ^^^ + break$0 'a; + } +} +"#, + ) + } + + #[test] + fn goto_def_on_break_with_label() { + check( + r#" +fn foo() { + 'outer: loop { + // ^^^^ + 'inner: loop { + 'innermost: loop { + } + break$0 'outer; + } + } +} +"#, + ); + } + + #[test] + fn goto_def_on_return_in_try() { + check( + r#" +fn main() { + fn f() { + // ^^ + try { + return$0; + } + + return; + } +} +"#, + ) + } + + #[test] + fn goto_def_on_break_in_try() { + check( + r#" +fn main() { + for i in 1..100 { + // ^^^ + let x: Result<(), ()> = try { + break$0; + }; + } +} +"#, + ) + } + + #[test] + fn goto_def_on_return_in_async_block() { + check( + r#" +fn main() { + async { + // ^^^^^ + return$0; + } +} +"#, + ) + } + + #[test] + fn goto_def_on_for_kw() { + check( + r#" +fn main() { + for$0 i in 1..5 {} + // ^^^ +} +"#, + ) + } + + #[test] + fn goto_def_on_fn_kw() { + check( + r#" +fn main() { + fn$0 foo() {} + // ^^ +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 76e5e9dd9288a..2eff7796d548b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -24,7 +24,7 @@ pub(crate) fn goto_implementation( FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = Semantics::new(db); - let source_file = sema.parse(file_id); + let source_file = sema.parse_guess_edition(file_id); let syntax = source_file.syntax().clone(); let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { @@ -117,7 +117,7 @@ fn impls_for_trait_item( #[cfg(test)] mod tests { - use ide_db::base_db::FileRange; + use ide_db::FileRange; use itertools::Itertools; use crate::fixture; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index ad393d98001b2..f75b8fb7d0224 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -21,7 +21,7 @@ pub(crate) fn goto_type_definition( ) -> Option>> { let sema = hir::Semantics::new(db); - let file: ast::SourceFile = sema.parse(file_id); + let file: ast::SourceFile = sema.parse_guess_edition(file_id); let token: SyntaxToken = pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 2, @@ -113,7 +113,7 @@ pub(crate) fn goto_type_definition( #[cfg(test)] mod tests { - use ide_db::base_db::FileRange; + use ide_db::FileRange; use itertools::Itertools; use crate::fixture; diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index a5689403ee14b..8fcd38b4e3435 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -1,24 +1,25 @@ use std::iter; -use hir::{DescendPreference, Semantics}; +use hir::{db, DescendPreference, FilePosition, FileRange, HirFileId, InFile, Semantics}; use ide_db::{ - base_db::{FileId, FilePosition, FileRange}, defs::{Definition, IdentClass}, helpers::pick_best_token, search::{FileReference, ReferenceCategory, SearchScope}, syntax_helpers::node_ext::{ - for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr, + eq_label_lt, for_each_tail_expr, full_path_of_name_ref, is_closure_or_blk_with_modif, + preorder_expr_with_ctx_checker, }, - FxHashSet, RootDatabase, + FxHashMap, FxHashSet, RootDatabase, }; +use span::EditionedFileId; use syntax::{ ast::{self, HasLoopBody}, match_ast, AstNode, SyntaxKind::{self, IDENT, INT_NUMBER}, - SyntaxToken, TextRange, T, + SyntaxToken, TextRange, WalkEvent, T, }; -use crate::{navigation_target::ToNav, NavigationTarget, TryToNav}; +use crate::{goto_definition, navigation_target::ToNav, NavigationTarget, TryToNav}; #[derive(PartialEq, Eq, Hash)] pub struct HighlightedRange { @@ -53,9 +54,12 @@ pub struct HighlightRelatedConfig { pub(crate) fn highlight_related( sema: &Semantics<'_, RootDatabase>, config: HighlightRelatedConfig, - pos @ FilePosition { offset, file_id }: FilePosition, + ide_db::FilePosition { offset, file_id }: ide_db::FilePosition, ) -> Option> { let _p = tracing::info_span!("highlight_related").entered(); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); let syntax = sema.parse(file_id).syntax().clone(); let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { @@ -69,19 +73,25 @@ pub(crate) fn highlight_related( // most if not all of these should be re-implemented with information seeded from hir match token.kind() { T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => { - highlight_exit_points(sema, token) + highlight_exit_points(sema, token).remove(&file_id) + } + T![fn] | T![return] | T![->] if config.exit_points => { + highlight_exit_points(sema, token).remove(&file_id) + } + T![await] | T![async] if config.yield_points => { + highlight_yield_points(sema, token).remove(&file_id) } - T![fn] | T![return] | T![->] if config.exit_points => highlight_exit_points(sema, token), - T![await] | T![async] if config.yield_points => highlight_yield_points(token), T![for] if config.break_points && token.parent().and_then(ast::ForExpr::cast).is_some() => { - highlight_break_points(token) + highlight_break_points(sema, token).remove(&file_id) } T![break] | T![loop] | T![while] | T![continue] if config.break_points => { - highlight_break_points(token) + highlight_break_points(sema, token).remove(&file_id) } T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), - _ if config.references => highlight_references(sema, token, pos), + _ if config.references => { + highlight_references(sema, token, FilePosition { file_id, offset }) + } _ => None, } } @@ -89,7 +99,7 @@ pub(crate) fn highlight_related( fn highlight_closure_captures( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, - file_id: FileId, + file_id: EditionedFileId, ) -> Option> { let closure = token.parent_ancestors().take(2).find_map(ast::ClosureExpr::cast)?; let search_range = closure.body()?.syntax().text_range(); @@ -271,50 +281,66 @@ fn highlight_references( } } -fn highlight_exit_points( +// If `file_id` is None, +pub(crate) fn highlight_exit_points( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, -) -> Option> { +) -> FxHashMap> { fn hl( sema: &Semantics<'_, RootDatabase>, - def_ranges: [Option; 2], - body: Option, - ) -> Option> { - let mut highlights = Vec::new(); - highlights.extend( - def_ranges - .into_iter() - .flatten() - .map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), - ); - let body = body?; - walk_expr(&body, &mut |expr| match expr { - ast::Expr::ReturnExpr(expr) => { - if let Some(token) = expr.return_token() { - highlights.push(HighlightedRange { - category: ReferenceCategory::empty(), - range: token.text_range(), - }); - } + def_token: Option, + body: ast::Expr, + ) -> Option>> { + let mut highlights: FxHashMap> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().insert(hrange); } - ast::Expr::TryExpr(try_) => { - if let Some(token) = try_.question_mark_token() { - highlights.push(HighlightedRange { - category: ReferenceCategory::empty(), - range: token.text_range(), - }); + }; + + if let Some(tok) = def_token { + let file_id = sema.hir_file_for(&tok.parent()?); + let range = Some(tok.text_range()); + push_to_highlights(file_id, range); + } + + WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| { + let file_id = sema.hir_file_for(expr.syntax()); + + let range = match &expr { + ast::Expr::TryExpr(try_) => { + try_.question_mark_token().map(|token| token.text_range()) } - } - ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) => { - if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) { - highlights.push(HighlightedRange { - category: ReferenceCategory::empty(), - range: expr.syntax().text_range(), - }); + ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) + if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) => + { + Some(expr.syntax().text_range()) } - } - _ => (), + _ => None, + }; + + push_to_highlights(file_id, range); }); + + // We should handle `return` separately, because when it is used in a `try` block, + // it will exit the outside function instead of the block itself. + WalkExpandedExprCtx::new(sema) + .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure) + .walk(&body, &mut |_, expr| { + let file_id = sema.hir_file_for(expr.syntax()); + + let range = match &expr { + ast::Expr::ReturnExpr(expr) => { + expr.return_token().map(|token| token.text_range()) + } + _ => None, + }; + + push_to_highlights(file_id, range); + }); + let tail = match body { ast::Expr::BlockExpr(b) => b.tail_expr(), e => Some(e), @@ -322,171 +348,188 @@ fn highlight_exit_points( if let Some(tail) = tail { for_each_tail_expr(&tail, &mut |tail| { + let file_id = sema.hir_file_for(tail.syntax()); let range = match tail { ast::Expr::BreakExpr(b) => b .break_token() .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()), _ => tail.syntax().text_range(), }; - highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }) + push_to_highlights(file_id, Some(range)); }); } Some(highlights) } - for anc in token.parent_ancestors() { - return match_ast! { - match anc { - ast::Fn(fn_) => hl(sema, [fn_.fn_token().map(|it| it.text_range()), None], fn_.body().map(ast::Expr::BlockExpr)), - ast::ClosureExpr(closure) => hl( - sema, - closure.param_list().map_or([None; 2], |p| [p.l_paren_token().map(|it| it.text_range()), p.r_paren_token().map(|it| it.text_range())]), - closure.body() - ), - ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) { - hl( - sema, - [block_expr.modifier().and_then(|modifier| match modifier { - ast::BlockModifier::Async(t) | ast::BlockModifier::Try(t) | ast::BlockModifier::Const(t) => Some(t.text_range()), - _ => None, - }), None], - Some(block_expr.into()) - ) - } else { - continue; + + let mut res = FxHashMap::default(); + for def in goto_definition::find_fn_or_blocks(sema, &token) { + let new_map = match_ast! { + match def { + ast::Fn(fn_) => fn_.body().and_then(|body| hl(sema, fn_.fn_token(), body.into())), + ast::ClosureExpr(closure) => { + let pipe_tok = closure.param_list().and_then(|p| p.pipe_token()); + closure.body().and_then(|body| hl(sema, pipe_tok, body)) + }, + ast::BlockExpr(blk) => match blk.modifier() { + Some(ast::BlockModifier::Async(t)) => hl(sema, Some(t), blk.into()), + Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => { + hl(sema, Some(t), blk.into()) + }, + _ => continue, }, _ => continue, } }; + merge_map(&mut res, new_map); } - None + + res.into_iter().map(|(file_id, ranges)| (file_id, ranges.into_iter().collect())).collect() } -fn highlight_break_points(token: SyntaxToken) -> Option> { - fn hl( +pub(crate) fn highlight_break_points( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> FxHashMap> { + pub(crate) fn hl( + sema: &Semantics<'_, RootDatabase>, cursor_token_kind: SyntaxKind, - token: Option, + loop_token: Option, label: Option, - body: Option, - ) -> Option> { - let mut highlights = Vec::new(); - let range = cover_range( - token.map(|tok| tok.text_range()), + expr: ast::Expr, + ) -> Option>> { + let mut highlights: FxHashMap> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().insert(hrange); + } + }; + + let label_lt = label.as_ref().and_then(|it| it.lifetime()); + + if let Some(range) = cover_range( + loop_token.as_ref().map(|tok| tok.text_range()), label.as_ref().map(|it| it.syntax().text_range()), - ); - highlights.extend( - range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), - ); - for_each_break_and_continue_expr(label, body, &mut |expr| { - let range: Option = match (cursor_token_kind, expr) { - (T![for] | T![while] | T![loop] | T![break], ast::Expr::BreakExpr(break_)) => { - cover_range( - break_.break_token().map(|it| it.text_range()), - break_.lifetime().map(|it| it.syntax().text_range()), - ) + ) { + let file_id = loop_token + .and_then(|tok| Some(sema.hir_file_for(&tok.parent()?))) + .unwrap_or_else(|| sema.hir_file_for(label.unwrap().syntax())); + push_to_highlights(file_id, Some(range)); + } + + WalkExpandedExprCtx::new(sema) + .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure) + .walk(&expr, &mut |depth, expr| { + let file_id = sema.hir_file_for(expr.syntax()); + + // Only highlight the `break`s for `break` and `continue`s for `continue` + let (token, token_lt) = match expr { + ast::Expr::BreakExpr(b) if cursor_token_kind != T![continue] => { + (b.break_token(), b.lifetime()) + } + ast::Expr::ContinueExpr(c) if cursor_token_kind != T![break] => { + (c.continue_token(), c.lifetime()) + } + _ => return, + }; + + if !(depth == 1 && token_lt.is_none() || eq_label_lt(&label_lt, &token_lt)) { + return; } - ( - T![for] | T![while] | T![loop] | T![continue], - ast::Expr::ContinueExpr(continue_), - ) => cover_range( - continue_.continue_token().map(|it| it.text_range()), - continue_.lifetime().map(|it| it.syntax().text_range()), - ), - _ => None, - }; - highlights.extend( - range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), - ); - }); + + let text_range = cover_range( + token.map(|it| it.text_range()), + token_lt.map(|it| it.syntax().text_range()), + ); + + push_to_highlights(file_id, text_range); + }); + Some(highlights) } - let parent = token.parent()?; - let lbl = match_ast! { - match parent { - ast::BreakExpr(b) => b.lifetime(), - ast::ContinueExpr(c) => c.lifetime(), - ast::LoopExpr(l) => l.label().and_then(|it| it.lifetime()), - ast::ForExpr(f) => f.label().and_then(|it| it.lifetime()), - ast::WhileExpr(w) => w.label().and_then(|it| it.lifetime()), - ast::BlockExpr(b) => Some(b.label().and_then(|it| it.lifetime())?), - _ => return None, - } - }; - let lbl = lbl.as_ref(); - let label_matches = |def_lbl: Option| match lbl { - Some(lbl) => { - Some(lbl.text()) == def_lbl.and_then(|it| it.lifetime()).as_ref().map(|it| it.text()) - } - None => true, + + let Some(loops) = goto_definition::find_loops(sema, &token) else { + return FxHashMap::default(); }; + + let mut res = FxHashMap::default(); let token_kind = token.kind(); - for anc in token.parent_ancestors().flat_map(ast::Expr::cast) { - return match anc { - ast::Expr::LoopExpr(l) if label_matches(l.label()) => hl( - token_kind, - l.loop_token(), - l.label(), - l.loop_body().and_then(|it| it.stmt_list()), - ), - ast::Expr::ForExpr(f) if label_matches(f.label()) => hl( - token_kind, - f.for_token(), - f.label(), - f.loop_body().and_then(|it| it.stmt_list()), - ), - ast::Expr::WhileExpr(w) if label_matches(w.label()) => hl( - token_kind, - w.while_token(), - w.label(), - w.loop_body().and_then(|it| it.stmt_list()), - ), - ast::Expr::BlockExpr(e) if e.label().is_some() && label_matches(e.label()) => { - hl(token_kind, None, e.label(), e.stmt_list()) - } + for expr in loops { + let new_map = match &expr { + ast::Expr::LoopExpr(l) => hl(sema, token_kind, l.loop_token(), l.label(), expr), + ast::Expr::ForExpr(f) => hl(sema, token_kind, f.for_token(), f.label(), expr), + ast::Expr::WhileExpr(w) => hl(sema, token_kind, w.while_token(), w.label(), expr), + ast::Expr::BlockExpr(e) => hl(sema, token_kind, None, e.label(), expr), _ => continue, }; + merge_map(&mut res, new_map); } - None + + res.into_iter().map(|(file_id, ranges)| (file_id, ranges.into_iter().collect())).collect() } -fn highlight_yield_points(token: SyntaxToken) -> Option> { +pub(crate) fn highlight_yield_points( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> FxHashMap> { fn hl( + sema: &Semantics<'_, RootDatabase>, async_token: Option, body: Option, - ) -> Option> { - let mut highlights = vec![HighlightedRange { - category: ReferenceCategory::empty(), - range: async_token?.text_range(), - }]; - if let Some(body) = body { - walk_expr(&body, &mut |expr| { - if let ast::Expr::AwaitExpr(expr) = expr { - if let Some(token) = expr.await_token() { - highlights.push(HighlightedRange { - category: ReferenceCategory::empty(), - range: token.text_range(), - }); - } - } - }); - } + ) -> Option>> { + let mut highlights: FxHashMap> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().insert(hrange); + } + }; + + let async_token = async_token?; + let async_tok_file_id = sema.hir_file_for(&async_token.parent()?); + push_to_highlights(async_tok_file_id, Some(async_token.text_range())); + + let Some(body) = body else { + return Some(highlights); + }; + + WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| { + let file_id = sema.hir_file_for(expr.syntax()); + + let text_range = match expr { + ast::Expr::AwaitExpr(expr) => expr.await_token(), + ast::Expr::ReturnExpr(expr) => expr.return_token(), + _ => None, + } + .map(|it| it.text_range()); + + push_to_highlights(file_id, text_range); + }); + Some(highlights) } - for anc in token.parent_ancestors() { - return match_ast! { + + let mut res = FxHashMap::default(); + for anc in goto_definition::find_fn_or_blocks(sema, &token) { + let new_map = match_ast! { match anc { - ast::Fn(fn_) => hl(fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)), + ast::Fn(fn_) => hl(sema, fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)), ast::BlockExpr(block_expr) => { if block_expr.async_token().is_none() { continue; } - hl(block_expr.async_token(), Some(block_expr.into())) + hl(sema, block_expr.async_token(), Some(block_expr.into())) }, - ast::ClosureExpr(closure) => hl(closure.async_token(), closure.body()), + ast::ClosureExpr(closure) => hl(sema, closure.async_token(), closure.body()), _ => continue, } }; + merge_map(&mut res, new_map); } - None + + res.into_iter().map(|(file_id, ranges)| (file_id, ranges.into_iter().collect())).collect() } fn cover_range(r0: Option, r1: Option) -> Option { @@ -506,6 +549,115 @@ fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSe .collect() } +fn original_frange( + db: &dyn db::ExpandDatabase, + file_id: HirFileId, + text_range: Option, +) -> Option { + InFile::new(file_id, text_range?).original_node_file_range_opt(db).map(|(frange, _)| frange) +} + +fn merge_map( + res: &mut FxHashMap>, + new: Option>>, +) { + let Some(new) = new else { + return; + }; + new.into_iter().for_each(|(file_id, ranges)| { + res.entry(file_id).or_default().extend(ranges); + }); +} + +/// Preorder walk all the expression's child expressions. +/// For macro calls, the callback will be called on the expanded expressions after +/// visiting the macro call itself. +struct WalkExpandedExprCtx<'a> { + sema: &'a Semantics<'a, RootDatabase>, + depth: usize, + check_ctx: &'static dyn Fn(&ast::Expr) -> bool, +} + +impl<'a> WalkExpandedExprCtx<'a> { + fn new(sema: &'a Semantics<'a, RootDatabase>) -> Self { + Self { sema, depth: 0, check_ctx: &is_closure_or_blk_with_modif } + } + + fn with_check_ctx(&self, check_ctx: &'static dyn Fn(&ast::Expr) -> bool) -> Self { + Self { check_ctx, ..*self } + } + + fn walk(&mut self, expr: &ast::Expr, cb: &mut dyn FnMut(usize, ast::Expr)) { + preorder_expr_with_ctx_checker(expr, self.check_ctx, &mut |ev: WalkEvent| { + match ev { + syntax::WalkEvent::Enter(expr) => { + cb(self.depth, expr.clone()); + + if Self::should_change_depth(&expr) { + self.depth += 1; + } + + if let ast::Expr::MacroExpr(expr) = expr { + if let Some(expanded) = + expr.macro_call().and_then(|call| self.sema.expand(&call)) + { + match_ast! { + match expanded { + ast::MacroStmts(it) => { + self.handle_expanded(it, cb); + }, + ast::Expr(it) => { + self.walk(&it, cb); + }, + _ => {} + } + } + } + } + } + syntax::WalkEvent::Leave(expr) if Self::should_change_depth(&expr) => { + self.depth -= 1; + } + _ => {} + } + false + }) + } + + fn handle_expanded(&mut self, expanded: ast::MacroStmts, cb: &mut dyn FnMut(usize, ast::Expr)) { + if let Some(expr) = expanded.expr() { + self.walk(&expr, cb); + } + + for stmt in expanded.statements() { + if let ast::Stmt::ExprStmt(stmt) = stmt { + if let Some(expr) = stmt.expr() { + self.walk(&expr, cb); + } + } + } + } + + fn should_change_depth(expr: &ast::Expr) -> bool { + match expr { + ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => true, + ast::Expr::BlockExpr(blk) if blk.label().is_some() => true, + _ => false, + } + } + + fn is_async_const_block_or_closure(expr: &ast::Expr) -> bool { + match expr { + ast::Expr::BlockExpr(b) => matches!( + b.modifier(), + Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Const(_)) + ), + ast::Expr::ClosureExpr(_) => true, + _ => false, + } + } +} + #[cfg(test)] mod tests { use itertools::Itertools; @@ -892,6 +1044,7 @@ impl Never { } macro_rules! never { () => { never() } + // ^^^^^^^ } fn never() -> ! { loop {} } fn foo() ->$0 u32 { @@ -1718,4 +1871,140 @@ fn test() { "#, ); } + + #[test] + fn return_in_macros() { + check( + r#" +macro_rules! N { + ($i:ident, $x:expr, $blk:expr) => { + for $i in 0..$x { + $blk + } + }; +} + +fn main() { + fn f() { + // ^^ + N!(i, 5, { + println!("{}", i); + return$0; + // ^^^^^^ + }); + + for i in 1..5 { + return; + // ^^^^^^ + } + (|| { + return; + })(); + } +} +"#, + ) + } + + #[test] + fn return_in_closure() { + check( + r#" +macro_rules! N { + ($i:ident, $x:expr, $blk:expr) => { + for $i in 0..$x { + $blk + } + }; +} + +fn main() { + fn f() { + N!(i, 5, { + println!("{}", i); + return; + }); + + for i in 1..5 { + return; + } + (|| { + // ^ + return$0; + // ^^^^^^ + })(); + } +} +"#, + ) + } + + #[test] + fn return_in_try() { + check( + r#" +fn main() { + fn f() { + // ^^ + try { + return$0; + // ^^^^^^ + } + + return; + // ^^^^^^ + } +} +"#, + ) + } + + #[test] + fn break_in_try() { + check( + r#" +fn main() { + for i in 1..100 { + // ^^^ + let x: Result<(), ()> = try { + break$0; + // ^^^^^ + }; + } +} +"#, + ) + } + + #[test] + fn no_highlight_on_return_in_macro_call() { + check( + r#" +//- minicore:include +//- /lib.rs +macro_rules! M { + ($blk:expr) => { + $blk + }; +} + +fn main() { + fn f() { + // ^^ + M!({ return$0; }); + // ^^^^^^ + // ^^^^^^^^^^^^^^^ + + include!("a.rs") + // ^^^^^^^^^^^^^^^^ + } +} + +//- /a.rs +{ + return; +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 2006baa30a8c3..500674e32b30a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -8,11 +8,10 @@ use std::{iter, ops::Not}; use either::Either; use hir::{db::DefDatabase, DescendPreference, HasCrate, HasSource, LangItem, Semantics}; use ide_db::{ - base_db::FileRange, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, - FxIndexSet, RootDatabase, + FileRange, FxIndexSet, RootDatabase, }; use itertools::{multizip, Itertools}; use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T}; @@ -110,7 +109,7 @@ pub(crate) fn hover( config: &HoverConfig, ) -> Option> { let sema = &hir::Semantics::new(db); - let file = sema.parse(file_id).syntax().clone(); + let file = sema.parse_guess_edition(file_id).syntax().clone(); let mut res = if range.is_empty() { hover_simple(sema, FilePosition { file_id, offset: range.start() }, file, config) } else { @@ -454,7 +453,7 @@ fn runnable_action( Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), Definition::Function(func) => { let src = func.source(sema.db)?; - if src.file_id != file_id.into() { + if src.file_id != file_id { cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr); return None; 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 5036770fec801..3257305184e91 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1,5 +1,5 @@ use expect_test::{expect, Expect}; -use ide_db::base_db::{FileLoader, FileRange}; +use ide_db::{base_db::FileLoader, FileRange}; use syntax::TextRange; use crate::{ @@ -2358,17 +2358,17 @@ fn test_hover_trait_show_qualifiers() { check_actions( r"unsafe trait foo$0() {}", expect![[r#" - [ - Implementation( - FilePosition { - file_id: FileId( - 0, - ), - offset: 13, - }, - ), - ] - "#]], + [ + Implementation( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 13, + }, + ), + ] + "#]], ); } @@ -2925,17 +2925,17 @@ fn test_hover_trait_has_impl_action() { check_actions( r#"trait foo$0() {}"#, expect![[r#" - [ - Implementation( - FilePosition { - file_id: FileId( - 0, - ), - offset: 6, - }, - ), - ] - "#]], + [ + Implementation( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 6, + }, + ), + ] + "#]], ); } @@ -2944,17 +2944,17 @@ fn test_hover_struct_has_impl_action() { check_actions( r"struct foo$0() {}", expect![[r#" - [ - Implementation( - FilePosition { - file_id: FileId( - 0, - ), - offset: 7, - }, - ), - ] - "#]], + [ + Implementation( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 7, + }, + ), + ] + "#]], ); } @@ -2963,17 +2963,17 @@ fn test_hover_union_has_impl_action() { check_actions( r#"union foo$0() {}"#, expect![[r#" - [ - Implementation( - FilePosition { - file_id: FileId( - 0, - ), - offset: 6, - }, - ), - ] - "#]], + [ + Implementation( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 6, + }, + ), + ] + "#]], ); } @@ -2982,17 +2982,17 @@ fn test_hover_enum_has_impl_action() { check_actions( r"enum foo$0() { A, B }", expect![[r#" - [ - Implementation( - FilePosition { - file_id: FileId( - 0, - ), - offset: 5, - }, - ), - ] - "#]], + [ + Implementation( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 5, + }, + ), + ] + "#]], ); } @@ -3001,17 +3001,17 @@ fn test_hover_self_has_impl_action() { check_actions( r#"struct foo where Self$0:;"#, expect![[r#" - [ - Implementation( - FilePosition { - file_id: FileId( - 0, - ), - offset: 7, - }, - ), - ] - "#]], + [ + Implementation( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 7, + }, + ), + ] + "#]], ); } @@ -3025,7 +3025,7 @@ fn foo_$0test() {} expect![[r#" [ Reference( - FilePosition { + FilePositionWrapper { file_id: FileId( 0, ), @@ -8450,7 +8450,7 @@ impl Iterator for S { expect![[r#" [ Implementation( - FilePosition { + FilePositionWrapper { file_id: FileId( 0, ), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 944951f26a2fd..0a8d2727575d2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -5,12 +5,13 @@ use std::{ use either::Either; use hir::{ - known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, + sym, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{famous_defs::FamousDefs, FileRange, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; +use span::EditionedFileId; use stdx::never; use syntax::{ ast::{self, AstNode}, @@ -493,6 +494,9 @@ pub(crate) fn inlay_hints( ) -> Vec { let _p = tracing::info_span!("inlay_hints").entered(); let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); let file = sema.parse(file_id); let file = file.syntax(); @@ -527,6 +531,9 @@ pub(crate) fn inlay_hints_resolve( ) -> Option { let _p = tracing::info_span!("inlay_hints_resolve").entered(); let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); let file = sema.parse(file_id); let file = file.syntax(); @@ -551,7 +558,7 @@ fn hints( hints: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - file_id: FileId, + file_id: EditionedFileId, node: SyntaxNode, ) { closing_brace::hints(hints, sema, config, file_id, node.clone()); @@ -633,7 +640,7 @@ fn hint_iterator( if ty.impls_trait(db, iter_trait, &[]) { let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item { - hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias), + hir::AssocItem::TypeAlias(alias) if alias.name(db) == sym::Item.clone() => Some(alias), _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 1118f11d99dd4..5775abaeb18c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -4,9 +4,10 @@ //! let _x /* i32 */= f(4, 4); //! ``` use hir::Semantics; -use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{famous_defs::FamousDefs, RootDatabase}; use itertools::Itertools; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode, HasGenericArgs, HasName}, match_ast, @@ -21,7 +22,7 @@ pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _file_id: FileId, + _file_id: EditionedFileId, pat: &ast::IdentPat, ) -> Option<()> { if !config.type_hints { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index d86487d4b41b8..4e15213b8bbf0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -1,11 +1,12 @@ //! Implementation of "chaining" inlay hints. use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode}, Direction, NodeOrToken, SyntaxKind, T, }; -use crate::{FileId, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; use super::label_of_ty; @@ -13,7 +14,7 @@ pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _file_id: FileId, + _file_id: EditionedFileId, expr: &ast::Expr, ) -> Option<()> { if !config.chaining_hints { @@ -142,7 +143,7 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -161,7 +162,7 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -225,7 +226,7 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -244,7 +245,7 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -292,7 +293,7 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -311,7 +312,7 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -360,7 +361,7 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -373,7 +374,7 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -392,7 +393,7 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -405,7 +406,7 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -456,7 +457,7 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 1, ), @@ -469,7 +470,7 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 1, ), @@ -488,7 +489,7 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 1, ), @@ -501,7 +502,7 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 1, ), @@ -520,7 +521,7 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 1, ), @@ -533,7 +534,7 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 1, ), @@ -552,7 +553,7 @@ fn main() { InlayHintLabelPart { text: "MyIter", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -600,7 +601,7 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -619,7 +620,7 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -638,7 +639,7 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), @@ -656,7 +657,7 @@ fn main() { InlayHintLabelPart { text: "self", linked_location: Some( - FileRange { + FileRangeWrapper { file_id: FileId( 0, ), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 2cefd5acdc2e4..ea96c9504e55c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -4,20 +4,21 @@ //! } /* fn g */ //! ``` use hir::{HirDisplay, Semantics}; -use ide_db::{base_db::FileRange, RootDatabase}; +use ide_db::{FileRange, RootDatabase}; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode, HasName}, match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{FileId, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, config: &InlayHintsConfig, - file_id: FileId, - node: SyntaxNode, + file_id: EditionedFileId, + mut node: SyntaxNode, ) -> Option<()> { let min_lines = config.closing_brace_hints_min_lines?; @@ -51,6 +52,15 @@ pub(super) fn hints( let module = ast::Module::cast(list.syntax().parent()?)?; (format!("mod {}", module.name()?), module.name().map(name)) + } else if let Some(label) = ast::Label::cast(node.clone()) { + // in this case, `ast::Label` could be seen as a part of `ast::BlockExpr` + // the actual number of lines in this case should be the line count of the parent BlockExpr, + // which the `min_lines` config cares about + node = node.parent()?; + let block = label.syntax().parent().and_then(ast::BlockExpr::cast)?; + closing_token = block.stmt_list()?.r_curly_token()?; + let lifetime = label.lifetime().map_or_else(String::new, |it| it.to_string()); + (lifetime, Some(label.syntax().text_range())) } else if let Some(block) = ast::BlockExpr::cast(node.clone()) { closing_token = block.stmt_list()?.r_curly_token()?; @@ -107,7 +117,7 @@ pub(super) fn hints( return None; } - let linked_location = name_range.map(|range| FileRange { file_id, range }); + let linked_location = name_range.map(|range| FileRange { file_id: file_id.into(), range }); acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, @@ -188,6 +198,29 @@ fn f() { ]; } //^ fn f +"#, + ); + } + + #[test] + fn hints_closing_brace_for_block_expr() { + check_with_config( + InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG }, + r#" +fn test() { + 'end: { + 'do_a: { + 'do_b: { + + } + //^ 'do_b + break 'end; + } + //^ 'do_a + } + //^ 'end + } +//^ fn test "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index f1b524e088095..e87e10d8504ed 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -1,7 +1,8 @@ //! Implementation of "closure return type" inlay hints. //! //! Tests live in [`bind_pat`][super::bind_pat] module. -use ide_db::{base_db::FileId, famous_defs::FamousDefs}; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use stdx::TupleExt; use syntax::ast::{self, AstNode}; use text_edit::{TextRange, TextSize}; @@ -12,7 +13,7 @@ pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _file_id: FileId, + _file_id: EditionedFileId, closure: ast::ClosureExpr, ) -> Option<()> { if !config.closure_capture_hints { @@ -73,7 +74,7 @@ pub(super) fn hints( ), None, source.name().and_then(|name| { - name.syntax().original_file_range_opt(sema.db).map(TupleExt::head) + name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) }), ); acc.push(InlayHint { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 3b41db0f13d0a..f6bd7ca064fda 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -1,7 +1,8 @@ //! Implementation of "closure return type" inlay hints. //! //! Tests live in [`bind_pat`][super::bind_pat] module. -use ide_db::{base_db::FileId, famous_defs::FamousDefs}; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::ast::{self, AstNode}; use crate::{ @@ -13,7 +14,7 @@ pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _file_id: FileId, + _file_id: EditionedFileId, closure: ast::ClosureExpr, ) -> Option<()> { if config.closure_return_type_hints == ClosureReturnTypeHints::Never { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index 202954100fb97..eca0ebe629f48 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -5,7 +5,8 @@ //! } //! ``` use hir::Semantics; -use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use span::EditionedFileId; use syntax::ast::{self, AstNode, HasName}; use crate::{ @@ -17,7 +18,7 @@ pub(super) fn enum_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - _: FileId, + _: EditionedFileId, enum_: ast::Enum, ) -> Option<()> { if let DiscriminantHints::Never = config.discriminant_hints { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 51855eeae232e..b60a80a8ac6b5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -46,7 +46,7 @@ pub(crate) fn hints( } let name = param.name(sema.db); - let param_name = name.as_str()?; + let param_name = name.as_str(); let should_hide = { let argument = get_string_representation(&arg)?; 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 31f0c79037453..7f901db28d368 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 @@ -10,11 +10,11 @@ use hir::{ mir::{MirSpan, TerminatorKind}, ChalkTyInterner, DefWithBody, Semantics, }; -use ide_db::{base_db::FileRange, RootDatabase}; +use ide_db::{FileRange, RootDatabase}; use syntax::{ ast::{self, AstNode}, - match_ast, + match_ast, ToSmolStr, }; use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; @@ -32,9 +32,8 @@ pub(super) fn hints( let def = sema.to_def(def)?; let def: DefWithBody = def.into(); - let source_map = sema.db.body_with_source_map(def.into()).1; + let (hir, source_map) = sema.db.body_with_source_map(def.into()); - let hir = sema.db.body(def.into()); let mir = sema.db.mir_body(def.into()).ok()?; let local_to_binding = mir.local_to_binding_map(); @@ -74,21 +73,34 @@ pub(super) fn hints( Ok(s) => s.value.text_range(), Err(_) => continue, }, + MirSpan::BindingId(b) => { + match source_map + .patterns_for_binding(b) + .iter() + .find_map(|p| source_map.pat_syntax(*p).ok()) + { + Some(s) => s.value.text_range(), + None => 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]; - let binding_source = binding - .definitions + let binding_source = source_map + .patterns_for_binding(*binding) .first() .and_then(|d| source_map.pat_syntax(*d).ok()) .and_then(|d| { - Some(FileRange { file_id: d.file_id.file_id()?, range: d.value.text_range() }) + Some(FileRange { + file_id: d.file_id.file_id()?.into(), + range: d.value.text_range(), + }) }); - let name = binding.name.to_smol_str(); + let binding = &hir.bindings[*binding]; + let name = binding.name.display_no_db().to_smolstr(); if name.starts_with(", + linked_location: Option, ) -> InlayHintLabel { let colon = if config.render_colons { ":" } else { "" }; - InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location) + InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into)) } fn get_callable( @@ -118,7 +121,7 @@ fn should_hide_param_name_hint( } let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => Some(it.name(sema.db).to_smol_str()), + hir::CallableKind::Function(it) => Some(it.name(sema.db).display_no_db().to_smolstr()), _ => None, }; let fn_name = fn_name.as_deref(); diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs index 7bd2da9f88a29..aeb3c8c1ee5ac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs @@ -1,8 +1,5 @@ use hir::Semantics; -use ide_db::{ - base_db::{FilePosition, SourceDatabaseExt}, - LineIndexDatabase, RootDatabase, -}; +use ide_db::{base_db::SourceDatabaseExt, FilePosition, LineIndexDatabase, RootDatabase}; use std::{fmt::Write, time::Instant}; use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; @@ -26,7 +23,7 @@ pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> S fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let source_file = sema.parse_guess_edition(position.file_id); let item = ancestors_at_offset(source_file.syntax(), position.offset) .filter(|it| !ast::MacroCall::can_cast(it.kind())) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index f0b35903f38de..8cb81a9cc452c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -43,7 +43,6 @@ mod parent_module; mod references; mod rename; mod runnables; -mod shuffle_crate_graph; mod signature_help; mod ssr; mod static_index; @@ -62,7 +61,7 @@ use std::panic::UnwindSafe; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::ChangeWithProcMacros; +use hir::{sym, ChangeWithProcMacros}; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, @@ -70,6 +69,7 @@ use ide_db::{ }, prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; +use span::EditionedFileId; use syntax::SourceFile; use triomphe::Arc; use view_memory_layout::{view_memory_layout, RecursiveMemoryLayout}; @@ -120,10 +120,7 @@ pub use ide_completion::{ Snippet, SnippetScope, }; pub use ide_db::{ - base_db::{ - Cancelled, CrateGraph, CrateId, FileChange, FileId, FilePosition, FileRange, SourceRoot, - SourceRootId, - }, + base_db::{Cancelled, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId}, documentation::Documentation, label::Label, line_index::{LineCol, LineIndex}, @@ -131,7 +128,7 @@ pub use ide_db::{ search::{ReferenceCategory, SearchScope}, source_change::{FileSystemEdit, SnippetEdit, SourceChange}, symbol_index::Query, - RootDatabase, SymbolKind, + FileId, FilePosition, FileRange, RootDatabase, SymbolKind, }; pub use ide_diagnostics::{ Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode, Severity, @@ -163,7 +160,7 @@ pub struct AnalysisHost { } impl AnalysisHost { - pub fn new(lru_capacity: Option) -> AnalysisHost { + pub fn new(lru_capacity: Option) -> AnalysisHost { AnalysisHost { db: RootDatabase::new(lru_capacity) } } @@ -171,11 +168,11 @@ impl AnalysisHost { AnalysisHost { db } } - pub fn update_lru_capacity(&mut self, lru_capacity: Option) { + pub fn update_lru_capacity(&mut self, lru_capacity: Option) { self.db.update_base_query_lru_capacities(lru_capacity); } - pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { + pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, u16>) { self.db.update_lru_capacities(lru_capacities); } @@ -204,10 +201,6 @@ impl AnalysisHost { pub fn raw_database_mut(&mut self) -> &mut RootDatabase { &mut self.db } - - pub fn shuffle_crate_graph(&mut self) { - shuffle_crate_graph::shuffle_crate_graph(&mut self.db); - } } impl Default for AnalysisHost { @@ -248,7 +241,7 @@ impl Analysis { // FIXME: cfg options // Default to enable test for single file. let mut cfg_options = CfgOptions::default(); - cfg_options.insert_atom("test".into()); + cfg_options.insert_atom(sym::test.clone()); crate_graph.add_crate_root( file_id, Edition::CURRENT, @@ -298,7 +291,8 @@ impl Analysis { /// Gets the syntax tree of the file. pub fn parse(&self, file_id: FileId) -> Cancellable { - self.with_db(|db| db.parse(file_id).tree()) + // FIXME editiojn + self.with_db(|db| db.parse(EditionedFileId::current_edition(file_id)).tree()) } /// Returns true if this file belongs to an immutable library. @@ -321,7 +315,7 @@ impl Analysis { /// supported). pub fn matching_brace(&self, position: FilePosition) -> Cancellable> { self.with_db(|db| { - let parse = db.parse(position.file_id); + let parse = db.parse(EditionedFileId::current_edition(position.file_id)); let file = parse.tree(); matching_brace::matching_brace(&file, position.offset) }) @@ -386,7 +380,7 @@ impl Analysis { /// stuff like trailing commas. pub fn join_lines(&self, config: &JoinLinesConfig, frange: FileRange) -> Cancellable { self.with_db(|db| { - let parse = db.parse(frange.file_id); + let parse = db.parse(EditionedFileId::current_edition(frange.file_id)); join_lines::join_lines(config, &parse.tree(), frange.range) }) } @@ -422,7 +416,12 @@ impl Analysis { /// Returns a tree representation of symbols in the file. Useful to draw a /// file outline. pub fn file_structure(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree())) + // FIXME: Edition + self.with_db(|db| { + file_structure::file_structure( + &db.parse(EditionedFileId::current_edition(file_id)).tree(), + ) + }) } /// Returns a list of the places in the file where type hints can be displayed. @@ -449,7 +448,11 @@ impl Analysis { /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) + self.with_db(|db| { + folding_ranges::folding_ranges( + &db.parse(EditionedFileId::current_edition(file_id)).tree(), + ) + }) } /// Fuzzy searches for a symbol. @@ -751,7 +754,7 @@ impl Analysis { ide_ssr::MatchFinder::in_context(db, resolve_context, selections)?; match_finder.add_rule(rule)?; let edits = if parse_only { Default::default() } else { match_finder.edits() }; - Ok(SourceChange::from(edits)) + Ok(SourceChange::from_iter(edits)) }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 68854c33cefa5..1b64bc926039e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -5,10 +5,10 @@ use core::fmt; use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, DescendPreference, MacroKind, Semantics}; use ide_db::{ - base_db::{CrateOrigin, FilePosition, LangCrateOrigin}, + base_db::{CrateOrigin, LangCrateOrigin}, defs::{Definition, IdentClass}, helpers::pick_best_token, - RootDatabase, + FilePosition, RootDatabase, }; use itertools::Itertools; use syntax::{AstNode, SyntaxKind::*, T}; @@ -133,7 +133,7 @@ pub(crate) fn moniker( FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = &Semantics::new(db); - let file = sema.parse(file_id).syntax().clone(); + let file = sema.parse_guess_edition(file_id).syntax().clone(); let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into(); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT @@ -408,7 +408,7 @@ pub(crate) fn def_to_moniker( }), ), }; - PackageInformation { name, repo, version } + PackageInformation { name: name.as_str().to_owned(), repo, version } }, }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index b955ea99f0c04..ea6cc9d6de232 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -1,7 +1,7 @@ use std::{iter::once, mem}; use hir::Semantics; -use ide_db::{base_db::FileRange, helpers::pick_best_token, RootDatabase}; +use ide_db::{helpers::pick_best_token, FileRange, RootDatabase}; use itertools::Itertools; use syntax::{algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange}; use text_edit::{TextEdit, TextEditBuilder}; @@ -30,7 +30,7 @@ pub(crate) fn move_item( direction: Direction, ) -> Option { let sema = Semantics::new(db); - let file = sema.parse(range.file_id); + let file = sema.parse_guess_edition(range.file_id); let item = if range.range.is_empty() { SyntaxElement::Token(pick_best_token( 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 bfd62e7624357..066141d36f16a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -9,15 +9,14 @@ use hir::{ HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, }; use ide_db::{ - base_db::{FileId, FileRange}, defs::Definition, documentation::{Documentation, HasDocs}, - RootDatabase, SymbolKind, + FileId, FileRange, RootDatabase, SymbolKind, }; use stdx::never; use syntax::{ ast::{self, HasName}, - format_smolstr, AstNode, SmolStr, SyntaxNode, TextRange, + format_smolstr, AstNode, SmolStr, SyntaxNode, TextRange, ToSmolStr, }; /// `NavigationTarget` represents an element in the editor's UI which you can @@ -98,7 +97,7 @@ impl NavigationTarget { db: &RootDatabase, module: hir::Module, ) -> UpmappingResult { - let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); + let name = module.name(db).map(|it| it.display_no_db().to_smolstr()).unwrap_or_default(); match module.declaration_source(db) { Some(InFile { value, file_id }) => { orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( @@ -153,7 +152,7 @@ impl NavigationTarget { ) } - fn from_syntax( + pub(crate) fn from_syntax( file_id: FileId, name: SmolStr, focus_range: Option, @@ -190,7 +189,7 @@ impl TryToNav for FileSymbol { .is_alias .then(|| self.def.name(db)) .flatten() - .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), + .map_or_else(|| self.name.clone(), |it| it.display_no_db().to_smolstr()), alias: self.is_alias.then(|| self.name.clone()), kind: Some(hir::ModuleDefId::from(self.def).into()), full_range, @@ -274,9 +273,9 @@ pub(crate) trait ToNavFromAst: Sized { fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option { match t.container(db) { - hir::ItemContainer::Trait(it) => Some(it.name(db).to_smol_str()), + hir::ItemContainer::Trait(it) => Some(it.name(db).display_no_db().to_smolstr()), // FIXME: Handle owners of blocks correctly here - hir::ItemContainer::Module(it) => it.name(db).map(|name| name.to_smol_str()), + hir::ItemContainer::Module(it) => it.name(db).map(|name| name.display_no_db().to_smolstr()), _ => None, } } @@ -367,7 +366,7 @@ impl ToNav for hir::Module { fn to_nav(&self, db: &RootDatabase) -> UpmappingResult { let InFile { file_id, value } = self.definition_source(db); - let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); + let name = self.name(db).map(|it| it.display_no_db().to_smolstr()).unwrap_or_default(); let (syntax, focus) = match &value { ModuleSource::SourceFile(node) => (node.syntax(), None), ModuleSource::Module(node) => (node.syntax(), node.name()), @@ -424,7 +423,10 @@ impl TryToNav for hir::ExternCrateDecl { |(FileRange { file_id, range: full_range }, focus_range)| { let mut res = NavigationTarget::from_syntax( file_id, - self.alias_or_name(db).unwrap_or_else(|| self.name(db)).to_smol_str(), + self.alias_or_name(db) + .unwrap_or_else(|| self.name(db)) + .display_no_db() + .to_smolstr(), focus_range, full_range, SymbolKind::Module, @@ -532,7 +534,7 @@ impl ToNav for LocalSource { orig_range_with_focus(db, file_id, node, name).map( |(FileRange { file_id, range: full_range }, focus_range)| { - let name = local.name(db).to_smol_str(); + let name = local.name(db).display_no_db().to_smolstr(); let kind = if local.is_self(db) { SymbolKind::SelfParam } else if local.is_param(db) { @@ -565,7 +567,7 @@ impl ToNav for hir::Local { impl TryToNav for hir::Label { fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.source(db)?; - let name = self.name(db).to_smol_str(); + let name = self.name(db).display_no_db().to_smolstr(); Some(orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()).map( |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { @@ -586,7 +588,7 @@ impl TryToNav for hir::Label { impl TryToNav for hir::TypeParam { fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.merge().source(db)?; - let name = self.name(db).to_smol_str(); + let name = self.name(db).display_no_db().to_smolstr(); let value = match value { Either::Left(ast::TypeOrConstParam::Type(x)) => Either::Left(x), @@ -628,7 +630,7 @@ impl TryToNav for hir::TypeOrConstParam { impl TryToNav for hir::LifetimeParam { fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.source(db)?; - let name = self.name(db).to_smol_str(); + let name = self.name(db).display_no_db().to_smolstr(); Some(orig_range(db, file_id, value.syntax()).map( |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { @@ -649,7 +651,7 @@ impl TryToNav for hir::LifetimeParam { impl TryToNav for hir::ConstParam { fn try_to_nav(&self, db: &RootDatabase) -> Option> { let InFile { file_id, value } = self.merge().source(db)?; - let name = self.name(db).to_smol_str(); + let name = self.name(db).display_no_db().to_smolstr(); let value = match value { Either::Left(ast::TypeOrConstParam::Const(x)) => x, @@ -708,7 +710,7 @@ impl IntoIterator for UpmappingResult { } impl UpmappingResult { - fn map(self, f: impl Fn(T) -> U) -> UpmappingResult { + pub(crate) fn map(self, f: impl Fn(T) -> U) -> UpmappingResult { UpmappingResult { call_site: f(self.call_site), def_site: self.def_site.map(f) } } } @@ -730,13 +732,13 @@ fn orig_range_with_focus( ) } -fn orig_range_with_focus_r( +pub(crate) fn orig_range_with_focus_r( db: &RootDatabase, hir_file: HirFileId, value: TextRange, - name: Option, + focus_range: Option, ) -> UpmappingResult<(FileRange, Option)> { - let Some(name) = name else { return orig_range_r(db, hir_file, value) }; + let Some(name) = focus_range 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; @@ -821,8 +823,8 @@ fn orig_range_with_focus_r( UpmappingResult { call_site: ( - call_site_range, - call_site_focus.and_then(|FileRange { file_id, range }| { + call_site_range.into(), + call_site_focus.and_then(|hir::FileRange { file_id, range }| { if call_site_range.file_id == file_id && call_site_range.range.contains_range(range) { Some(range) @@ -833,8 +835,8 @@ fn orig_range_with_focus_r( ), def_site: def_site.map(|(def_site_range, def_site_focus)| { ( - def_site_range, - def_site_focus.and_then(|FileRange { file_id, range }| { + def_site_range.into(), + def_site_focus.and_then(|hir::FileRange { file_id, range }| { if def_site_range.file_id == file_id && def_site_range.range.contains_range(range) { @@ -854,7 +856,7 @@ fn orig_range( value: &SyntaxNode, ) -> UpmappingResult<(FileRange, Option)> { UpmappingResult { - call_site: (InFile::new(hir_file, value).original_file_range_rooted(db), None), + call_site: (InFile::new(hir_file, value).original_file_range_rooted(db).into(), None), def_site: None, } } @@ -865,7 +867,7 @@ fn orig_range_r( value: TextRange, ) -> UpmappingResult<(FileRange, Option)> { UpmappingResult { - call_site: (InFile::new(hir_file, value).original_node_file_range(db).0, None), + call_site: (InFile::new(hir_file, value).original_node_file_range(db).0.into(), None), def_site: None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index ce7a6779e27c2..74c50fcac3528 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -1,7 +1,7 @@ use hir::{db::DefDatabase, Semantics}; use ide_db::{ - base_db::{CrateId, FileId, FileLoader, FilePosition}, - RootDatabase, + base_db::{CrateId, FileLoader}, + FileId, FilePosition, RootDatabase, }; use itertools::Itertools; use syntax::{ @@ -26,7 +26,7 @@ use crate::NavigationTarget; /// This returns `Vec` because a module may be included from several places. pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let source_file = sema.parse_guess_edition(position.file_id); let mut module = find_node_at_offset::(source_file.syntax(), position.offset); @@ -66,7 +66,7 @@ pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec { #[cfg(test)] mod tests { - use ide_db::base_db::FileRange; + use ide_db::FileRange; use crate::fixture; diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 6f9e1b3740c0e..46c2d47ee827f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -11,10 +11,9 @@ use hir::{DescendPreference, PathResolution, Semantics}; use ide_db::{ - base_db::FileId, defs::{Definition, NameClass, NameRefClass}, search::{ReferenceCategory, SearchScope, UsageSearchResult}, - RootDatabase, + FileId, RootDatabase, }; use itertools::Itertools; use nohash_hasher::IntMap; @@ -25,7 +24,7 @@ use syntax::{ SyntaxNode, TextRange, TextSize, T, }; -use crate::{FilePosition, NavigationTarget, TryToNav}; +use crate::{highlight_related, FilePosition, HighlightedRange, NavigationTarget, TryToNav}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { @@ -56,7 +55,7 @@ pub(crate) fn find_all_refs( search_scope: Option, ) -> Option> { let _p = tracing::info_span!("find_all_refs").entered(); - let syntax = sema.parse(position.file_id).syntax().clone(); + let syntax = sema.parse_guess_edition(position.file_id).syntax().clone(); let make_searcher = |literal_search: bool| { move |def: Definition| { let mut usages = @@ -70,7 +69,7 @@ pub(crate) fn find_all_refs( .into_iter() .map(|(file_id, refs)| { ( - file_id, + file_id.into(), refs.into_iter() .map(|file_ref| (file_ref.range, file_ref.category)) .unique() @@ -104,6 +103,11 @@ pub(crate) fn find_all_refs( } }; + // Find references for control-flow keywords. + if let Some(res) = handle_control_flow_keywords(sema, position) { + return Some(vec![res]); + } + match name_for_constructor_search(&syntax, position) { Some(name) => { let def = match NameClass::classify(sema, &name)? { @@ -297,10 +301,42 @@ fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool { }).unwrap_or(false) } +fn handle_control_flow_keywords( + sema: &Semantics<'_, RootDatabase>, + FilePosition { file_id, offset }: FilePosition, +) -> Option { + let file = sema.parse_guess_edition(file_id); + let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword())?; + + let references = match token.kind() { + T![fn] | T![return] | T![try] => highlight_related::highlight_exit_points(sema, token), + T![async] => highlight_related::highlight_yield_points(sema, token), + T![loop] | T![while] | T![break] | T![continue] => { + highlight_related::highlight_break_points(sema, token) + } + T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => { + highlight_related::highlight_break_points(sema, token) + } + _ => return None, + } + .into_iter() + .map(|(file_id, ranges)| { + let ranges = ranges + .into_iter() + .map(|HighlightedRange { range, category }| (range, category)) + .collect(); + (file_id.into(), ranges) + }) + .collect(); + + Some(ReferenceSearchResult { declaration: None, references }) +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ide_db::base_db::FileId; + use ide_db::FileId; + use span::EditionedFileId; use stdx::format_to; use crate::{fixture, SearchScope}; @@ -941,7 +977,7 @@ pub(super) struct Foo$0 { check_with_scope( code, - Some(SearchScope::single_file(FileId::from_raw(2))), + Some(SearchScope::single_file(EditionedFileId::current_edition(FileId::from_raw(2)))), expect![[r#" quux Function FileId(0) 19..35 26..30 @@ -1200,7 +1236,7 @@ impl Foo { let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap(); let mut actual = String::new(); - for refs in refs { + for mut refs in refs { actual += "\n\n"; if let Some(decl) = refs.declaration { @@ -1211,7 +1247,8 @@ impl Foo { actual += "\n\n"; } - for (file_id, references) in &refs.references { + for (file_id, references) in &mut refs.references { + references.sort_by_key(|(range, _)| range.start()); for (range, category) in references { format_to!(actual, "{:?} {:?}", file_id, range); for (name, _flag) in category.iter_names() { @@ -2187,4 +2224,264 @@ fn test() { "#]], ); } + + #[test] + fn goto_ref_fn_kw() { + check( + r#" +macro_rules! N { + ($i:ident, $x:expr, $blk:expr) => { + for $i in 0..$x { + $blk + } + }; +} + +fn main() { + $0fn f() { + N!(i, 5, { + println!("{}", i); + return; + }); + + for i in 1..5 { + return; + } + + (|| { + return; + })(); + } +} +"#, + expect![[r#" + FileId(0) 136..138 + FileId(0) 207..213 + FileId(0) 264..270 + "#]], + ) + } + + #[test] + fn goto_ref_exit_points() { + check( + r#" +fn$0 foo() -> u32 { + if true { + return 0; + } + + 0?; + 0xDEAD_BEEF +} +"#, + expect![[r#" + FileId(0) 0..2 + FileId(0) 40..46 + FileId(0) 62..63 + FileId(0) 69..80 + "#]], + ); + } + + #[test] + fn test_ref_yield_points() { + check( + r#" +pub async$0 fn foo() { + let x = foo() + .await + .await; + || { 0.await }; + (async { 0.await }).await +} +"#, + expect![[r#" + FileId(0) 4..9 + FileId(0) 48..53 + FileId(0) 63..68 + FileId(0) 114..119 + "#]], + ); + } + + #[test] + fn goto_ref_for_kw() { + check( + r#" +fn main() { + $0for i in 1..5 { + break; + continue; + } +} +"#, + expect![[r#" + FileId(0) 16..19 + FileId(0) 40..45 + FileId(0) 55..63 + "#]], + ) + } + + #[test] + fn goto_ref_on_break_kw() { + check( + r#" +fn main() { + for i in 1..5 { + $0break; + continue; + } +} +"#, + expect![[r#" + FileId(0) 16..19 + FileId(0) 40..45 + "#]], + ) + } + + #[test] + fn goto_ref_on_break_kw_for_block() { + check( + r#" +fn main() { + 'a:{ + $0break 'a; + } +} +"#, + expect![[r#" + FileId(0) 16..19 + FileId(0) 29..37 + "#]], + ) + } + + #[test] + fn goto_ref_on_break_with_label() { + check( + r#" +fn foo() { + 'outer: loop { + break; + 'inner: loop { + 'innermost: loop { + } + $0break 'outer; + break; + } + break; + } +} +"#, + expect![[r#" + FileId(0) 15..27 + FileId(0) 39..44 + FileId(0) 127..139 + FileId(0) 178..183 + "#]], + ); + } + + #[test] + fn goto_ref_on_return_in_try() { + check( + r#" +fn main() { + fn f() { + try { + $0return; + } + + return; + } + return; +} +"#, + expect![[r#" + FileId(0) 16..18 + FileId(0) 51..57 + FileId(0) 78..84 + "#]], + ) + } + + #[test] + fn goto_ref_on_break_in_try() { + check( + r#" +fn main() { + for i in 1..100 { + let x: Result<(), ()> = try { + $0break; + }; + } +} +"#, + expect![[r#" + FileId(0) 16..19 + FileId(0) 84..89 + "#]], + ) + } + + #[test] + fn goto_ref_on_return_in_async_block() { + check( + r#" +fn main() { + $0async { + return; + } +} +"#, + expect![[r#" + FileId(0) 16..21 + FileId(0) 32..38 + "#]], + ) + } + + #[test] + fn goto_ref_on_return_in_macro_call() { + check( + r#" +//- minicore:include +//- /lib.rs +macro_rules! M { + ($blk:expr) => { + fn f() { + $blk + } + + $blk + }; +} + +fn main() { + M!({ + return$0; + }); + + f(); + include!("a.rs") +} + +//- /a.rs +{ + return; +} +"#, + expect![[r#" + FileId(0) 46..48 + FileId(0) 106..108 + FileId(0) 122..149 + FileId(0) 135..141 + FileId(0) 165..181 + FileId(1) 6..12 + "#]], + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 3d08e2f37182a..9581474ca7bd2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -6,16 +6,17 @@ use hir::{AsAssocItem, HirFileIdExt, InFile, Semantics}; use ide_db::{ - base_db::{FileId, FileRange}, defs::{Definition, NameClass, NameRefClass}, rename::{bail, format_err, source_edit_from_references, IdentifierKind}, source_change::SourceChangeBuilder, - RootDatabase, + FileId, FileRange, RootDatabase, }; use itertools::Itertools; +use span::Edition; use stdx::{always, never}; use syntax::{ ast, utils::is_raw_identifier, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange, TextSize, + ToSmolStr, }; use text_edit::TextEdit; @@ -33,7 +34,7 @@ pub(crate) fn prepare_rename( position: FilePosition, ) -> RenameResult> { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let source_file = sema.parse_guess_edition(position.file_id); let syntax = source_file.syntax(); let res = find_definitions(&sema, syntax, position)? @@ -87,7 +88,10 @@ pub(crate) fn rename( new_name: &str, ) -> RenameResult { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let file_id = sema + .attach_first_edition(position.file_id) + .ok_or_else(|| format_err!("No references found at position"))?; + let source_file = sema.parse(file_id); let syntax = source_file.syntax(); let defs = find_definitions(&sema, syntax, position)?; @@ -98,7 +102,7 @@ pub(crate) fn rename( // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can // properly find "direct" usages/references. .map(|(.., def)| { - match IdentifierKind::classify(new_name)? { + match IdentifierKind::classify(Edition::CURRENT_FIXME, new_name)? { IdentifierKind::Ident => (), IdentifierKind::Lifetime => { bail!("Cannot alias reference to a lifetime identifier") @@ -109,7 +113,7 @@ pub(crate) fn rename( let mut usages = def.usages(&sema).all(); // FIXME: hack - removes the usage that triggered this rename operation. - match usages.references.get_mut(&position.file_id).and_then(|refs| { + match usages.references.get_mut(&file_id).and_then(|refs| { refs.iter() .position(|ref_| ref_.range.contains_inclusive(position.offset)) .map(|idx| refs.remove(idx)) @@ -119,9 +123,9 @@ pub(crate) fn rename( }; let mut source_change = SourceChange::default(); - source_change.extend(usages.references.get_mut(&position.file_id).iter().map( - |refs| (position.file_id, source_edit_from_references(refs, def, new_name)), - )); + source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { + (position.file_id, source_edit_from_references(refs, def, new_name)) + })); Ok(source_change) }) @@ -266,7 +270,7 @@ fn find_definitions( // if the name differs from the definitions name it has to be an alias if def .name(sema.db) - .map_or(false, |it| it.to_smol_str() != name_ref.text().as_str()) + .map_or(false, |it| it.display_no_db().to_smolstr() != name_ref.text().as_str()) { Err(format_err!("Renaming aliases is currently unsupported")) } else { @@ -300,7 +304,11 @@ fn find_definitions( Err(format_err!("No references found at position")) } else { // remove duplicates, comparing `Definition`s - Ok(v.into_iter().unique_by(|&(.., def)| def).collect::>().into_iter()) + Ok(v.into_iter() + .unique_by(|&(.., def)| def) + .map(|(a, b, c)| (a.into(), b, c)) + .collect::>() + .into_iter()) } } Err(e) => Err(e), @@ -368,8 +376,8 @@ fn rename_to_self( let def = Definition::Local(local); let usages = def.usages(sema).all(); let mut source_change = SourceChange::default(); - source_change.extend(usages.iter().map(|(&file_id, references)| { - (file_id, source_edit_from_references(references, def, "self")) + source_change.extend(usages.iter().map(|(file_id, references)| { + (file_id.into(), source_edit_from_references(references, def, "self")) })); source_change.insert_source_edit( file_id.original_file(sema.db), @@ -390,7 +398,7 @@ fn rename_self_to_param( return Ok(SourceChange::default()); } - let identifier_kind = IdentifierKind::classify(new_name)?; + let identifier_kind = IdentifierKind::classify(Edition::CURRENT_FIXME, new_name)?; let InFile { file_id, value: self_param } = sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?; @@ -404,8 +412,8 @@ fn rename_self_to_param( } let mut source_change = SourceChange::default(); source_change.insert_source_edit(file_id.original_file(sema.db), edit); - source_change.extend(usages.iter().map(|(&file_id, references)| { - (file_id, source_edit_from_references(references, def, new_name)) + source_change.extend(usages.iter().map(|(file_id, references)| { + (file_id.into(), source_edit_from_references(references, def, new_name)) })); Ok(source_change) } diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index a68ee4f8671f1..5d4b8b3643943 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -3,23 +3,22 @@ use std::fmt; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ - db::HirDatabase, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics, + db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ - base_db::{FilePosition, FileRange}, defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, search::{FileReferenceNode, SearchScope}, - FxHashMap, FxHashSet, RootDatabase, SymbolKind, + FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind, }; use itertools::Itertools; use span::TextSize; use stdx::{always, format_to}; use syntax::{ ast::{self, AstNode}, - SmolStr, SyntaxNode, + SmolStr, SyntaxNode, ToSmolStr, }; use crate::{references, FileId, NavigationTarget, ToNav, TryToNav}; @@ -229,7 +228,7 @@ pub(crate) fn related_tests( ) -> Vec { let sema = Semantics::new(db); let mut res: FxHashSet = FxHashSet::default(); - let syntax = sema.parse(position.file_id).syntax().clone(); + let syntax = sema.parse_guess_edition(position.file_id).syntax().clone(); find_related_tests(&sema, &syntax, position, search_scope, &mut res); @@ -290,8 +289,9 @@ fn find_related_tests_in_module( 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: mod_source.value }); - let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() }; + let mod_scope = SearchScope::file_range(hir::FileRange { file_id, range: mod_source.value }); + let fn_pos = + FilePosition { file_id: file_id.into(), offset: fn_name.syntax().text_range().start() }; find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests) } @@ -332,7 +332,7 @@ pub(crate) fn runnable_fn( }; canonical_path .map(TestId::Path) - .unwrap_or(TestId::Name(def.name(sema.db).to_smol_str())) + .unwrap_or(TestId::Name(def.name(sema.db).display_no_db().to_smolstr())) }; if def.is_test(sema.db) { @@ -403,7 +403,7 @@ pub(crate) fn runnable_impl( } fn has_cfg_test(attrs: AttrsWithOwner) -> bool { - attrs.cfgs().any(|cfg| matches!(cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if s == "test")) + attrs.cfgs().any(|cfg| matches!(&cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if *s == sym::test)) } /// Creates a test mod runnable for outline modules at the top of their definition. @@ -481,7 +481,8 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { Some(path) })(); - let test_id = path.map_or_else(|| TestId::Name(def_name.to_smol_str()), TestId::Path); + let test_id = + path.map_or_else(|| TestId::Name(def_name.display_no_db().to_smolstr()), TestId::Path); let mut nav = match def { Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def), diff --git a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs deleted file mode 100644 index 453d1836e16e4..0000000000000 --- a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs +++ /dev/null @@ -1,58 +0,0 @@ -use hir::{db::ExpandDatabase, ProcMacros}; -use ide_db::{ - base_db::{salsa::Durability, CrateGraph, SourceDatabase}, - FxHashMap, RootDatabase, -}; -use triomphe::Arc; - -// Feature: Shuffle Crate Graph -// -// Randomizes all crate IDs in the crate graph, for debugging. -// -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Shuffle Crate Graph** -// |=== -pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { - let crate_graph = db.crate_graph(); - let proc_macros = db.proc_macros(); - - let mut shuffled_ids = crate_graph.iter().collect::>(); - - let mut rng = oorandom::Rand32::new(stdx::rand::seed()); - stdx::rand::shuffle(&mut shuffled_ids, |i| rng.rand_range(0..i as u32) as usize); - - let mut new_graph = CrateGraph::default(); - let mut new_proc_macros = ProcMacros::default(); - - let mut map = FxHashMap::default(); - for old_id in shuffled_ids.iter().copied() { - let data = &crate_graph[old_id]; - let new_id = new_graph.add_crate_root( - data.root_file_id, - data.edition, - data.display_name.clone(), - data.version.clone(), - data.cfg_options.clone(), - data.potential_cfg_options.clone(), - data.env.clone(), - data.is_proc_macro, - data.origin.clone(), - ); - new_proc_macros.insert(new_id, proc_macros[&old_id].clone()); - map.insert(old_id, new_id); - } - - for old_id in shuffled_ids.iter().copied() { - let data = &crate_graph[old_id]; - for dep in &data.dependencies { - let mut new_dep = dep.clone(); - new_dep.crate_id = map[&dep.crate_id]; - new_graph.add_dep(map[&old_id], new_dep).unwrap(); - } - } - - db.set_crate_graph_with_durability(Arc::new(new_graph), Durability::HIGH); - db.set_proc_macros_with_durability(Arc::new(new_proc_macros), Durability::HIGH); -} diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index c5eaacdb10d5d..b6c9e2f6366a7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -10,16 +10,15 @@ use hir::{ }; use ide_db::{ active_parameter::{callable_for_node, generic_def_for_node}, - base_db::FilePosition, documentation::{Documentation, HasDocs}, - FxIndexMap, + FilePosition, FxIndexMap, }; use stdx::format_to; use syntax::{ algo, ast::{self, AstChildren, HasArgList}, match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken, - TextRange, TextSize, T, + TextRange, TextSize, ToSmolStr, T, }; use crate::RootDatabase; @@ -74,7 +73,7 @@ pub(crate) fn signature_help( FilePosition { file_id, offset }: FilePosition, ) -> Option { let sema = Semantics::new(db); - let file = sema.parse(file_id); + let file = sema.parse_guess_edition(file_id); let file = file.syntax(); let token = file .token_at_offset(offset) @@ -379,7 +378,7 @@ fn add_assoc_type_bindings( for item in tr.items_with_supertraits(db) { if let AssocItem::TypeAlias(ty) = item { - let name = ty.name(db).to_smol_str(); + let name = ty.name(db).display_no_db().to_smolstr(); if !present_bindings.contains(&*name) { buf.clear(); format_to!(buf, "{} = …", name); @@ -660,7 +659,7 @@ mod tests { use std::iter; use expect_test::{expect, Expect}; - use ide_db::base_db::FilePosition; + use ide_db::FilePosition; use stdx::format_to; use test_fixture::ChangeFixture; @@ -674,7 +673,7 @@ mod tests { let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); - (database, FilePosition { file_id, offset }) + (database, FilePosition { file_id: file_id.into(), offset }) } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs index b49fe391bf258..41cc9c067d351 100644 --- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs +++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs @@ -3,7 +3,7 @@ //! depend on the ide_ssr crate. use ide_assists::{Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel}; -use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; +use ide_db::{label::Label, source_change::SourceChange, FileRange, RootDatabase}; pub(crate) fn ssr_assists( db: &RootDatabase, @@ -26,7 +26,7 @@ pub(crate) fn ssr_assists( SourceChange::from_text_edit(frange.file_id, text_edit_for_file) }; - let source_change_for_workspace = SourceChange::from(match_finder.edits()); + let source_change_for_workspace = SourceChange::from_iter(match_finder.edits()); (Some(source_change_for_file), Some(source_change_for_workspace)) } else { @@ -45,7 +45,7 @@ pub(crate) fn ssr_assists( group: Some(GroupLabel("Apply SSR".into())), target: comment_range, source_change, - trigger_signature_help: false, + command: None, }; ssr_assists.push(assist); @@ -59,9 +59,8 @@ mod tests { use expect_test::expect; use ide_assists::{Assist, AssistResolveStrategy}; use ide_db::{ - base_db::{salsa::Durability, FileRange}, - symbol_index::SymbolsDatabase, - FxHashSet, RootDatabase, + base_db::salsa::Durability, symbol_index::SymbolsDatabase, FileRange, FxHashSet, + RootDatabase, }; use test_fixture::WithFixture; use triomphe::Arc; @@ -73,7 +72,11 @@ mod tests { let mut local_roots = FxHashSet::default(); local_roots.insert(test_fixture::WORKSPACE); db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); - ssr_assists(&db, &resolve, FileRange { file_id, range: range_or_offset.into() }) + ssr_assists( + &db, + &resolve, + FileRange { file_id: file_id.into(), range: range_or_offset.into() }, + ) } #[test] @@ -143,7 +146,7 @@ mod tests { is_snippet: false, }, ), - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_file_assist); @@ -196,7 +199,7 @@ mod tests { is_snippet: false, }, ), - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_workspace_assist); @@ -236,7 +239,7 @@ mod tests { ), target: 10..21, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_file_assist); @@ -256,7 +259,7 @@ mod tests { ), target: 10..21, source_change: None, - trigger_signature_help: false, + command: None, } "#]] .assert_debug_eq(&apply_in_workspace_assist); diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 5eb5c87f13e39..cd9b7ae2f6209 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -3,12 +3,9 @@ use hir::{db::HirDatabase, Crate, HirFileIdExt, Module, Semantics}; use ide_db::{ - base_db::{FileId, FileRange, SourceDatabaseExt}, - defs::Definition, - documentation::Documentation, - famous_defs::FamousDefs, - helpers::get_definition, - FxHashMap, FxHashSet, RootDatabase, + base_db::SourceDatabaseExt, defs::Definition, documentation::Documentation, + famous_defs::FamousDefs, helpers::get_definition, FileId, FileRange, FxHashMap, FxHashSet, + RootDatabase, }; use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T}; @@ -160,7 +157,7 @@ impl StaticIndex<'_> { .unwrap(); // hovers let sema = hir::Semantics::new(self.db); - let tokens_or_nodes = sema.parse(file_id).syntax().clone(); + let tokens_or_nodes = sema.parse_guess_edition(file_id).syntax().clone(); let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), @@ -234,7 +231,7 @@ impl StaticIndex<'_> { let db = &*analysis.db; let work = all_modules(db).into_iter().filter(|module| { let file_id = module.definition_source_file_id(db).original_file(db); - let source_root = db.file_source_root(file_id); + let source_root = db.file_source_root(file_id.into()); let source_root = db.source_root(source_root); !source_root.is_library }); @@ -251,7 +248,7 @@ impl StaticIndex<'_> { if visited_files.contains(&file_id) { continue; } - this.add_file(file_id); + this.add_file(file_id.into()); // mark the file visited_files.insert(file_id); } @@ -262,7 +259,7 @@ impl StaticIndex<'_> { #[cfg(test)] mod tests { use crate::{fixture, StaticIndex}; - use ide_db::{base_db::FileRange, FxHashSet}; + use ide_db::{FileRange, FxHashSet}; use syntax::TextSize; fn check_all_ranges(ra_fixture: &str) { diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index b998c0bfc6513..67d6932da962d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -10,7 +10,7 @@ use ide_db::{ debug::{DebugQueryTable, TableEntry}, Query, QueryTable, }, - CompressedFileTextQuery, CrateData, FileId, ParseQuery, SourceDatabase, SourceRootId, + CompressedFileTextQuery, CrateData, ParseQuery, SourceDatabase, SourceRootId, }, symbol_index::ModuleSymbolsQuery, }; @@ -20,6 +20,7 @@ use ide_db::{ }; use itertools::Itertools; use profile::{memory_usage, Bytes}; +use span::{EditionedFileId, FileId}; use stdx::format_to; use syntax::{ast, Parse, SyntaxNode}; use triomphe::Arc; @@ -209,8 +210,8 @@ impl fmt::Display for SyntaxTreeStats { } } -impl StatCollect> for SyntaxTreeStats { - fn collect_entry(&mut self, _: FileId, value: Option>) { +impl StatCollect> for SyntaxTreeStats { + fn collect_entry(&mut self, _: EditionedFileId, value: Option>) { self.total += 1; self.retained += value.is_some() as usize; } 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 fd8e6f4046582..23185920058b7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -15,6 +15,7 @@ mod tests; use hir::{DescendPreference, Name, Semantics}; use ide_db::{FxHashMap, RootDatabase, SymbolKind}; +use span::EditionedFileId; use syntax::{ ast::{self, IsString}, AstNode, AstToken, NodeOrToken, @@ -188,11 +189,14 @@ pub(crate) fn highlight( ) -> Vec { let _p = tracing::info_span!("highlight").entered(); let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); // Determine the root based on the given range. let (root, range_to_highlight) = { - let source_file = sema.parse(file_id); - let source_file = source_file.syntax(); + let file = sema.parse(file_id); + let source_file = file.syntax(); match range_to_highlight { Some(range) => { let node = match source_file.covering_element(range) { @@ -218,7 +222,7 @@ fn traverse( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, config: HighlightConfig, - file_id: FileId, + file_id: EditionedFileId, root: &SyntaxNode, krate: hir::Crate, range_to_highlight: TextRange, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index c73b6acb0d073..291073f87735c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -6,6 +6,7 @@ use ide_db::{ defs::{Definition, IdentClass, NameClass, NameRefClass}, FxHashMap, RootDatabase, SymbolKind, }; +use stdx::hash_once; use syntax::{ ast, match_ast, AstNode, AstToken, NodeOrToken, SyntaxKind::{self, *}, @@ -358,17 +359,7 @@ fn highlight_name( } fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { - fn hash(x: T) -> u64 { - use ide_db::FxHasher; - - use std::hash::Hasher; - - let mut hasher = FxHasher::default(); - x.hash(&mut hasher); - hasher.finish() - } - - hash((name, shadow_count)) + hash_once::((name.as_str(), shadow_count)) } pub(super) fn highlight_def( 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 e754b702deed1..47ad54759a872 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 @@ -1,7 +1,8 @@ //! Renders a bit of code as HTML. -use ide_db::base_db::SourceDatabase; +use hir::Semantics; use oorandom::Rand32; +use span::EditionedFileId; use stdx::format_to; use syntax::AstNode; @@ -11,8 +12,12 @@ use crate::{ }; pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { - let parse = db.parse(file_id); - + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let file = file.syntax(); fn rainbowify(seed: u64) -> String { let mut rng = Rand32::new(seed); format!( @@ -35,10 +40,10 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo macro_bang: true, syntactic_name_ref_highlighting: false, }, - file_id, + file_id.into(), None, ); - let text = parse.tree().syntax().to_string(); + let text = file.to_string(); let mut buf = String::new(); buf.push_str(STYLE); buf.push_str("
");
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index f9b8a22a3c090..bc1ec530076ce 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -3,11 +3,12 @@
 use std::mem;
 
 use either::Either;
-use hir::{InFile, Semantics};
+use hir::{sym, HirFileId, InFile, Semantics};
 use ide_db::{
-    active_parameter::ActiveParameter, base_db::FileId, defs::Definition,
-    documentation::docs_with_rangemap, rust_doc::is_rust_fence, SymbolKind,
+    active_parameter::ActiveParameter, defs::Definition, documentation::docs_with_rangemap,
+    rust_doc::is_rust_fence, SymbolKind,
 };
+use span::EditionedFileId;
 use syntax::{
     ast::{self, AstNode, IsString, QuoteOffsets},
     AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize,
@@ -108,14 +109,14 @@ pub(super) fn doc_comment(
     hl: &mut Highlights,
     sema: &Semantics<'_, RootDatabase>,
     config: HighlightConfig,
-    src_file_id: FileId,
+    src_file_id: EditionedFileId,
     node: &SyntaxNode,
 ) {
     let (attributes, def) = match doc_attributes(sema, node) {
         Some(it) => it,
         None => return,
     };
-    let src_file_id = src_file_id.into();
+    let src_file_id: HirFileId = src_file_id.into();
 
     // Extract intra-doc links and emit highlights for them.
     if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) {
@@ -153,7 +154,7 @@ pub(super) fn doc_comment(
     let mut new_comments = Vec::new();
     let mut string;
 
-    for attr in attributes.by_key("doc").attrs() {
+    for attr in attributes.by_key(&sym::doc).attrs() {
         let InFile { file_id, value: src } = attrs_source_map.source_of(attr);
         if file_id != src_file_id {
             continue;
@@ -271,7 +272,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option {
             // We gotta hunt the string token manually here
-            let text = attr.string_value()?;
+            let text = attr.string_value()?.as_str();
             // FIXME: We just pick the first string literal that has the same text as the doc attribute
             // This means technically we might highlight the wrong one
             it.syntax()
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 5f711600a2907..2070022d4183c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -1238,7 +1238,7 @@ fn benchmark_syntax_highlighting_parser() {
             })
             .count()
     };
-    assert_eq!(hash, 1169);
+    assert_eq!(hash, 1167);
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
index 05cdf430efbb2..e241cb82bd5b2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
@@ -1,7 +1,5 @@
-use ide_db::{
-    base_db::{FileId, SourceDatabase},
-    RootDatabase,
-};
+use hir::Semantics;
+use ide_db::{FileId, RootDatabase};
 use syntax::{
     AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
 };
@@ -22,9 +20,10 @@ pub(crate) fn syntax_tree(
     file_id: FileId,
     text_range: Option,
 ) -> String {
-    let parse = db.parse(file_id);
+    let sema = Semantics::new(db);
+    let parse = sema.parse_guess_edition(file_id);
     if let Some(text_range) = text_range {
-        let node = match parse.tree().syntax().covering_element(text_range) {
+        let node = match parse.syntax().covering_element(text_range) {
             NodeOrToken::Node(node) => node,
             NodeOrToken::Token(token) => {
                 if let Some(tree) = syntax_tree_for_string(&token, text_range) {
@@ -36,7 +35,7 @@ pub(crate) fn syntax_tree(
 
         format!("{node:#?}")
     } else {
-        format!("{:#?}", parse.tree().syntax())
+        format!("{:#?}", parse.syntax())
     }
 }
 
@@ -88,7 +87,7 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option Option {
-    let parse = db.parse(position.file_id);
+    let parse = db.parse(EditionedFileId::current_edition(position.file_id));
     let file = parse.tree();
     let token = file.syntax().token_at_offset(position.offset).left_biased()?;
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
index 51cf45bd22b15..fe532f4cc55ee 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
@@ -1,6 +1,5 @@
 use hir::{DefWithBody, Semantics};
-use ide_db::base_db::FilePosition;
-use ide_db::RootDatabase;
+use ide_db::{FilePosition, RootDatabase};
 use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Hir
@@ -17,7 +16,7 @@ pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
 
 fn body_hir(db: &RootDatabase, position: FilePosition) -> Option {
     let sema = Semantics::new(db);
-    let source_file = sema.parse(position.file_id);
+    let source_file = sema.parse_guess_edition(position.file_id);
 
     let item = ancestors_at_offset(source_file.syntax(), position.offset)
         .filter(|it| !ast::MacroCall::can_cast(it.kind()))
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index e072df430fc0d..dae79998dc4eb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -1,6 +1,6 @@
-use hir::db::DefDatabase;
-use ide_db::base_db::FileId;
-use ide_db::RootDatabase;
+use hir::{db::DefDatabase, Semantics};
+use ide_db::{FileId, RootDatabase};
+use span::EditionedFileId;
 
 // Feature: Debug ItemTree
 //
@@ -12,5 +12,9 @@ use ide_db::RootDatabase;
 // | VS Code | **rust-analyzer: Debug ItemTree**
 // |===
 pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
+    let sema = Semantics::new(db);
+    let file_id = sema
+        .attach_first_edition(file_id)
+        .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
     db.file_item_tree(file_id.into()).pretty_print(db)
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
index 826447d058dff..df3f2f18b4cdf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
@@ -64,11 +64,7 @@ enum FieldOrTupleIdx {
 impl FieldOrTupleIdx {
     fn name(&self, db: &RootDatabase) -> String {
         match *self {
-            FieldOrTupleIdx::Field(f) => f
-                .name(db)
-                .as_str()
-                .map(|s| s.to_owned())
-                .unwrap_or_else(|| format!(".{}", f.name(db).as_tuple_index().unwrap())),
+            FieldOrTupleIdx::Field(f) => f.name(db).as_str().to_owned(),
             FieldOrTupleIdx::TupleIdx(i) => format!(".{i}"),
         }
     }
@@ -88,7 +84,7 @@ pub(crate) fn view_memory_layout(
     position: FilePosition,
 ) -> Option {
     let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id);
+    let file = sema.parse_guess_edition(position.file_id);
     let token =
         pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
             SyntaxKind::IDENT => 3,
@@ -189,14 +185,7 @@ pub(crate) fn view_memory_layout(
                 | Definition::SelfType(_) => "[ROOT]".to_owned(),
 
                 // def is an item
-                def => def
-                    .name(db)
-                    .map(|n| {
-                        n.as_str()
-                            .map(|s| s.to_owned())
-                            .unwrap_or_else(|| format!(".{}", n.as_tuple_index().unwrap()))
-                    })
-                    .unwrap_or("[ROOT]".to_owned()),
+                def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()),
             };
 
             let typename = ty.display(db).to_string();
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
index 5fb47039890bc..7a228375d5e97 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
@@ -1,6 +1,5 @@
 use hir::{DefWithBody, Semantics};
-use ide_db::base_db::FilePosition;
-use ide_db::RootDatabase;
+use ide_db::{FilePosition, RootDatabase};
 use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Mir
@@ -16,7 +15,7 @@ pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String {
 
 fn body_mir(db: &RootDatabase, position: FilePosition) -> Option {
     let sema = Semantics::new(db);
-    let source_file = sema.parse(position.file_id);
+    let source_file = sema.parse_guess_edition(position.file_id);
 
     let item = ancestors_at_offset(source_file.syntax(), position.offset)
         .filter(|it| !ast::MacroCall::can_cast(it.kind()))
diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml
index 67b4164ce1fe0..c08ecb5c307b1 100644
--- a/src/tools/rust-analyzer/crates/intern/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml
@@ -18,6 +18,7 @@ dashmap.workspace = true
 hashbrown.workspace = true
 rustc-hash.workspace = true
 triomphe.workspace = true
+sptr = "0.3.2"
 
 [lints]
-workspace = true
\ No newline at end of file
+workspace = true
diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs
index 40d18b1cf8677..58327419f6314 100644
--- a/src/tools/rust-analyzer/crates/intern/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs
@@ -20,6 +20,9 @@ type Guard = dashmap::RwLockWriteGuard<
     HashMap, SharedValue<()>, BuildHasherDefault>,
 >;
 
+mod symbol;
+pub use self::symbol::{symbols as sym, Symbol};
+
 pub struct Interned {
     arc: Arc,
 }
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
new file mode 100644
index 0000000000000..ef76192ba83ed
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
@@ -0,0 +1,360 @@
+//! Attempt at flexible symbol interning, allowing to intern and free strings at runtime while also
+//! supporting compile time declaration of symbols that will never be freed.
+
+use std::{
+    borrow::Borrow,
+    fmt,
+    hash::{BuildHasherDefault, Hash, Hasher},
+    mem::{self, ManuallyDrop},
+    ptr::NonNull,
+    sync::OnceLock,
+};
+
+use dashmap::{DashMap, SharedValue};
+use hashbrown::{hash_map::RawEntryMut, HashMap};
+use rustc_hash::FxHasher;
+use sptr::Strict;
+use triomphe::Arc;
+
+pub mod symbols;
+
+// some asserts for layout compatibility
+const _: () = assert!(std::mem::size_of::>() == std::mem::size_of::<&str>());
+const _: () = assert!(std::mem::align_of::>() == std::mem::align_of::<&str>());
+
+const _: () = assert!(std::mem::size_of::>>() == std::mem::size_of::<&&str>());
+const _: () = assert!(std::mem::align_of::>>() == std::mem::align_of::<&&str>());
+
+const _: () =
+    assert!(std::mem::size_of::<*const *const str>() == std::mem::size_of::());
+const _: () =
+    assert!(std::mem::align_of::<*const *const str>() == std::mem::align_of::());
+
+const _: () = assert!(std::mem::size_of::>>() == std::mem::size_of::());
+const _: () =
+    assert!(std::mem::align_of::>>() == std::mem::align_of::());
+
+/// A pointer that points to a pointer to a `str`, it may be backed as a `&'static &'static str` or
+/// `Arc>` but its size is that of a thin pointer. The active variant is encoded as a tag
+/// in the LSB of the alignment niche.
+// Note, Ideally this would encode a `ThinArc` and `ThinRef`/`ThinConstPtr` instead of the double indirection.
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
+struct TaggedArcPtr {
+    packed: NonNull<*const str>,
+}
+
+unsafe impl Send for TaggedArcPtr {}
+unsafe impl Sync for TaggedArcPtr {}
+
+impl TaggedArcPtr {
+    const BOOL_BITS: usize = true as usize;
+
+    const fn non_arc(r: &'static &'static str) -> Self {
+        assert!(
+            mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS
+        );
+        // SAFETY: The pointer is non-null as it is derived from a reference
+        // Ideally we would call out to `pack_arc` but for a `false` tag, unfortunately the
+        // packing stuff requires reading out the pointer to an integer which is not supported
+        // in const contexts, so here we make use of the fact that for the non-arc version the
+        // tag is false (0) and thus does not need touching the actual pointer value.ext)
+
+        let packed =
+            unsafe { NonNull::new_unchecked((r as *const &str).cast::<*const str>().cast_mut()) };
+        Self { packed }
+    }
+
+    fn arc(arc: Arc>) -> Self {
+        assert!(
+            mem::align_of::<&'static &'static str>().trailing_zeros() as usize > Self::BOOL_BITS
+        );
+        Self {
+            packed: Self::pack_arc(
+                // Safety: `Arc::into_raw` always returns a non null pointer
+                unsafe { NonNull::new_unchecked(Arc::into_raw(arc).cast_mut().cast()) },
+            ),
+        }
+    }
+
+    /// Retrieves the tag.
+    #[inline]
+    pub(crate) fn try_as_arc_owned(self) -> Option>>> {
+        // Unpack the tag from the alignment niche
+        let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS;
+        if tag != 0 {
+            // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc`
+            Some(ManuallyDrop::new(unsafe {
+                Arc::from_raw(self.pointer().as_ptr().cast::>())
+            }))
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> {
+        let packed_tag = true as usize;
+
+        // can't use this strict provenance stuff here due to trait methods not being const
+        // unsafe {
+        //     // Safety: The pointer is derived from a non-null
+        //     NonNull::new_unchecked(Strict::map_addr(ptr.as_ptr(), |addr| {
+        //         // Safety:
+        //         // - The pointer is `NonNull` => it's address is `NonZero`
+        //         // - `P::BITS` least significant bits are always zero (`Pointer` contract)
+        //         // - `T::BITS <= P::BITS` (from `Self::ASSERTION`)
+        //         //
+        //         // Thus `addr >> T::BITS` is guaranteed to be non-zero.
+        //         //
+        //         // `{non_zero} | packed_tag` can't make the value zero.
+
+        //         (addr >> Self::BOOL_BITS) | packed_tag
+        //     }))
+        // }
+        // so what follows is roughly what the above looks like but inlined
+
+        let self_addr = ptr.as_ptr() as *const *const str as usize;
+        let addr = self_addr | packed_tag;
+        let dest_addr = addr as isize;
+        let offset = dest_addr.wrapping_sub(self_addr as isize);
+
+        // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes
+        unsafe { NonNull::new_unchecked(ptr.as_ptr().cast::().wrapping_offset(offset).cast()) }
+    }
+
+    #[inline]
+    pub(crate) fn pointer(self) -> NonNull<*const str> {
+        // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes
+        unsafe {
+            NonNull::new_unchecked(Strict::map_addr(self.packed.as_ptr(), |addr| {
+                addr & !Self::BOOL_BITS
+            }))
+        }
+    }
+
+    #[inline]
+    pub(crate) fn as_str(&self) -> &str {
+        // SAFETY: We always point to a pointer to a str no matter what variant is active
+        unsafe { *self.pointer().as_ptr().cast::<&str>() }
+    }
+}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Symbol {
+    repr: TaggedArcPtr,
+}
+
+impl fmt::Debug for Symbol {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.as_str().fmt(f)
+    }
+}
+
+const _: () = assert!(std::mem::size_of::() == std::mem::size_of::>());
+const _: () = assert!(std::mem::align_of::() == std::mem::align_of::>());
+
+static MAP: OnceLock>> = OnceLock::new();
+
+impl Symbol {
+    pub fn intern(s: &str) -> Self {
+        let (mut shard, hash) = Self::select_shard(s);
+        // Atomically,
+        // - check if `obj` is already in the map
+        //   - if so, copy out its entry, conditionally bumping the backing Arc and return it
+        //   - if not, put it into a box and then into an Arc, insert it, bump the ref-count and return the copy
+        // This needs to be atomic (locking the shard) to avoid races with other thread, which could
+        // insert the same object between us looking it up and inserting it.
+        match shard.raw_entry_mut().from_key_hashed_nocheck(hash, s) {
+            RawEntryMut::Occupied(occ) => Self { repr: increase_arc_refcount(occ.key().0) },
+            RawEntryMut::Vacant(vac) => Self {
+                repr: increase_arc_refcount(
+                    vac.insert_hashed_nocheck(
+                        hash,
+                        SymbolProxy(TaggedArcPtr::arc(Arc::new(Box::::from(s)))),
+                        SharedValue::new(()),
+                    )
+                    .0
+                     .0,
+                ),
+            },
+        }
+    }
+
+    pub fn integer(i: usize) -> Self {
+        match i {
+            0 => symbols::INTEGER_0.clone(),
+            1 => symbols::INTEGER_1.clone(),
+            2 => symbols::INTEGER_2.clone(),
+            3 => symbols::INTEGER_3.clone(),
+            4 => symbols::INTEGER_4.clone(),
+            5 => symbols::INTEGER_5.clone(),
+            6 => symbols::INTEGER_6.clone(),
+            7 => symbols::INTEGER_7.clone(),
+            8 => symbols::INTEGER_8.clone(),
+            9 => symbols::INTEGER_9.clone(),
+            10 => symbols::INTEGER_10.clone(),
+            11 => symbols::INTEGER_11.clone(),
+            12 => symbols::INTEGER_12.clone(),
+            13 => symbols::INTEGER_13.clone(),
+            14 => symbols::INTEGER_14.clone(),
+            15 => symbols::INTEGER_15.clone(),
+            i => Symbol::intern(&format!("{i}")),
+        }
+    }
+
+    pub fn empty() -> Self {
+        symbols::__empty.clone()
+    }
+
+    pub fn as_str(&self) -> &str {
+        self.repr.as_str()
+    }
+
+    #[inline]
+    fn select_shard(
+        s: &str,
+    ) -> (
+        dashmap::RwLockWriteGuard<
+            'static,
+            HashMap, BuildHasherDefault>,
+        >,
+        u64,
+    ) {
+        let storage = MAP.get_or_init(symbols::prefill);
+        let hash = {
+            let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher());
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let shard_idx = storage.determine_shard(hash as usize);
+        let shard = &storage.shards()[shard_idx];
+        (shard.write(), hash)
+    }
+
+    #[cold]
+    fn drop_slow(arc: &Arc>) {
+        let (mut shard, hash) = Self::select_shard(arc);
+
+        match Arc::count(arc) {
+            0 => unreachable!(),
+            1 => unreachable!(),
+            2 => (),
+            _ => {
+                // Another thread has interned another copy
+                return;
+            }
+        }
+
+        ManuallyDrop::into_inner(
+            match shard.raw_entry_mut().from_key_hashed_nocheck::(hash, arc.as_ref()) {
+                RawEntryMut::Occupied(occ) => occ.remove_entry(),
+                RawEntryMut::Vacant(_) => unreachable!(),
+            }
+            .0
+             .0
+            .try_as_arc_owned()
+            .unwrap(),
+        );
+        debug_assert_eq!(Arc::count(arc), 1);
+
+        // Shrink the backing storage if the shard is less than 50% occupied.
+        if shard.len() * 2 < shard.capacity() {
+            shard.shrink_to_fit();
+        }
+    }
+}
+
+impl Drop for Symbol {
+    #[inline]
+    fn drop(&mut self) {
+        let Some(arc) = self.repr.try_as_arc_owned() else {
+            return;
+        };
+        // When the last `Ref` is dropped, remove the object from the global map.
+        if Arc::count(&arc) == 2 {
+            // Only `self` and the global map point to the object.
+
+            Self::drop_slow(&arc);
+        }
+        // decrement the ref count
+        ManuallyDrop::into_inner(arc);
+    }
+}
+
+impl Clone for Symbol {
+    fn clone(&self) -> Self {
+        Self { repr: increase_arc_refcount(self.repr) }
+    }
+}
+
+fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr {
+    let Some(arc) = repr.try_as_arc_owned() else {
+        return repr;
+    };
+    // increase the ref count
+    mem::forget(Arc::clone(&arc));
+    repr
+}
+
+impl fmt::Display for Symbol {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.as_str().fmt(f)
+    }
+}
+
+// only exists so we can use `from_key_hashed_nocheck` with a &str
+#[derive(Debug, PartialEq, Eq)]
+struct SymbolProxy(TaggedArcPtr);
+
+impl Hash for SymbolProxy {
+    fn hash(&self, state: &mut H) {
+        self.0.as_str().hash(state);
+    }
+}
+
+impl Borrow for SymbolProxy {
+    fn borrow(&self) -> &str {
+        self.0.as_str()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn smoke_test() {
+        Symbol::intern("isize");
+        let base_len = MAP.get().unwrap().len();
+        let hello = Symbol::intern("hello");
+        let world = Symbol::intern("world");
+        let more_worlds = world.clone();
+        let bang = Symbol::intern("!");
+        let q = Symbol::intern("?");
+        assert_eq!(MAP.get().unwrap().len(), base_len + 4);
+        let bang2 = Symbol::intern("!");
+        assert_eq!(MAP.get().unwrap().len(), base_len + 4);
+        drop(bang2);
+        assert_eq!(MAP.get().unwrap().len(), base_len + 4);
+        drop(q);
+        assert_eq!(MAP.get().unwrap().len(), base_len + 3);
+        let default = Symbol::intern("default");
+        let many_worlds = world.clone();
+        assert_eq!(MAP.get().unwrap().len(), base_len + 3);
+        assert_eq!(
+            "hello default world!",
+            format!("{} {} {}{}", hello.as_str(), default.as_str(), world.as_str(), bang.as_str())
+        );
+        drop(default);
+        assert_eq!(
+            "hello world!",
+            format!("{} {}{}", hello.as_str(), world.as_str(), bang.as_str())
+        );
+        drop(many_worlds);
+        drop(more_worlds);
+        drop(hello);
+        drop(world);
+        drop(bang);
+        assert_eq!(MAP.get().unwrap().len(), base_len);
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
new file mode 100644
index 0000000000000..2feca32ff8686
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -0,0 +1,462 @@
+//! Module defining all known symbols required by the rest of rust-analyzer.
+#![allow(non_upper_case_globals)]
+
+use std::hash::{BuildHasherDefault, Hash as _, Hasher as _};
+
+use dashmap::{DashMap, SharedValue};
+use rustc_hash::FxHasher;
+
+use crate::{
+    symbol::{SymbolProxy, TaggedArcPtr},
+    Symbol,
+};
+
+macro_rules! define_symbols {
+    (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => {
+        // Ideally we would be emitting `const` here, but then we no longer have stable addresses
+        // which is what we are relying on for equality! In the future if consts can refer to
+        // statics we should swap these for `const`s and have the the string literal being pointed
+        // to be statics to refer to such that their address is stable.
+        $(
+            pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) };
+        )*
+        $(
+            pub static $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) };
+        )*
+
+
+        pub(super) fn prefill() -> DashMap> {
+            let mut dashmap_ = >>::with_hasher(BuildHasherDefault::default());
+
+            let hash_thing_ = |hasher_: &BuildHasherDefault, it_: &SymbolProxy| {
+                let mut hasher_ = std::hash::BuildHasher::build_hasher(hasher_);
+                it_.hash(&mut hasher_);
+                hasher_.finish()
+            };
+            {
+                $(
+
+                    let proxy_ = SymbolProxy($name.repr);
+                    let hash_ = hash_thing_(dashmap_.hasher(), &proxy_);
+                    let shard_idx_ = dashmap_.determine_shard(hash_ as usize);
+                    dashmap_.shards_mut()[shard_idx_].get_mut().raw_entry_mut().from_hash(hash_, |k| k == &proxy_).insert(proxy_, SharedValue::new(()));
+                )*
+                $(
+
+                    let proxy_ = SymbolProxy($alias.repr);
+                    let hash_ = hash_thing_(dashmap_.hasher(), &proxy_);
+                    let shard_idx_ = dashmap_.determine_shard(hash_ as usize);
+                    dashmap_.shards_mut()[shard_idx_].get_mut().raw_entry_mut().from_hash(hash_, |k| k == &proxy_).insert(proxy_, SharedValue::new(()));
+                )*
+            }
+            dashmap_
+        }
+    };
+}
+define_symbols! {
+    @WITH_NAME:
+
+    dotdotdot = "...",
+    INTEGER_0 = "0",
+    INTEGER_1 = "1",
+    INTEGER_2 = "2",
+    INTEGER_3 = "3",
+    INTEGER_4 = "4",
+    INTEGER_5 = "5",
+    INTEGER_6 = "6",
+    INTEGER_7 = "7",
+    INTEGER_8 = "8",
+    INTEGER_9 = "9",
+    INTEGER_10 = "10",
+    INTEGER_11 = "11",
+    INTEGER_12 = "12",
+    INTEGER_13 = "13",
+    INTEGER_14 = "14",
+    INTEGER_15 = "15",
+    __empty = "",
+    unsafe_ = "unsafe",
+    in_ = "in",
+    super_ = "super",
+    self_ = "self",
+    Self_ = "Self",
+    tick_static = "'static",
+    dollar_crate = "$crate",
+    MISSING_NAME = "[missing name]",
+    fn_ = "fn",
+    crate_ = "crate",
+    underscore = "_",
+    true_ = "true",
+    false_ = "false",
+    let_ = "let",
+    const_ = "const",
+    proc_dash_macro = "proc-macro",
+    aapcs_dash_unwind = "aapcs-unwind",
+    avr_dash_interrupt = "avr-interrupt",
+    avr_dash_non_dash_blocking_dash_interrupt = "avr-non-blocking-interrupt",
+    C_dash_cmse_dash_nonsecure_dash_call = "C-cmse-nonsecure-call",
+    C_dash_unwind = "C-unwind",
+    cdecl_dash_unwind = "cdecl-unwind",
+    fastcall_dash_unwind = "fastcall-unwind",
+    msp430_dash_interrupt = "msp430-interrupt",
+    platform_dash_intrinsic = "platform-intrinsic",
+    ptx_dash_kernel = "ptx-kernel",
+    riscv_dash_interrupt_dash_m = "riscv-interrupt-m",
+    riscv_dash_interrupt_dash_s = "riscv-interrupt-s",
+    rust_dash_call = "rust-call",
+    rust_dash_cold = "rust-cold",
+    rust_dash_intrinsic = "rust-intrinsic",
+    stdcall_dash_unwind = "stdcall-unwind",
+    system_dash_unwind = "system-unwind",
+    sysv64_dash_unwind = "sysv64-unwind",
+    thiscall_dash_unwind = "thiscall-unwind",
+    vectorcall_dash_unwind = "vectorcall-unwind",
+    win64_dash_unwind = "win64-unwind",
+    x86_dash_interrupt = "x86-interrupt",
+
+    @PLAIN:
+    __ra_fixup,
+    aapcs,
+    add_assign,
+    add,
+    alias,
+    align_offset,
+    align,
+    all,
+    alloc_layout,
+    alloc,
+    allow_internal_unsafe,
+    allow,
+    any,
+    as_str,
+    asm,
+    assert,
+    attributes,
+    begin_panic,
+    bench,
+    bitand_assign,
+    bitand,
+    bitor_assign,
+    bitor,
+    bitxor_assign,
+    bitxor,
+    bool,
+    box_free,
+    Box,
+    boxed,
+    branch,
+    Break,
+    c_void,
+    C,
+    call_mut,
+    call_once,
+    call,
+    cdecl,
+    Center,
+    cfg_accessible,
+    cfg_attr,
+    cfg_eval,
+    cfg,
+    char,
+    clone,
+    Clone,
+    coerce_unsized,
+    column,
+    compile_error,
+    concat_bytes,
+    concat_idents,
+    concat,
+    const_format_args,
+    const_panic_fmt,
+    const_param_ty,
+    Context,
+    Continue,
+    copy,
+    Copy,
+    core_panic,
+    core,
+    coroutine_state,
+    coroutine,
+    count,
+    crate_type,
+    CStr,
+    debug_assertions,
+    Debug,
+    default,
+    Default,
+    deprecated,
+    deref_mut,
+    deref_target,
+    deref,
+    derive_const,
+    derive,
+    discriminant_kind,
+    discriminant_type,
+    dispatch_from_dyn,destruct,
+    div_assign,
+    div,
+    doc,
+    drop_in_place,
+    drop,
+    dyn_metadata,
+    efiapi,
+    eh_catch_typeinfo,
+    eh_personality,
+    env,
+    eq,
+    Eq,
+    Err,
+    exchange_malloc,
+    exhaustive_patterns,
+    export_name,
+    f128,
+    f16,
+    f32,
+    f64,
+    fastcall,
+    feature,
+    file,
+    filter_map,
+    fmt,
+    fn_mut,
+    fn_once_output,
+    fn_once,
+    fn_ptr_addr,
+    fn_ptr_trait,
+    format_alignment,
+    format_args_nl,
+    format_args,
+    format_argument,
+    format_arguments,
+    format_count,
+    format_placeholder,
+    format_unsafe_arg,
+    format,
+    freeze,
+    from_output,
+    from_residual,
+    from_usize,
+    from_yeet,
+    fundamental,
+    future_trait,
+    future,
+    Future,
+    ge,
+    get_context,
+    global_allocator,
+    global_asm,
+    gt,
+    Hash,
+    hidden,
+    html_root_url,
+    i128,
+    i16,
+    i32,
+    i64,
+    i8,
+    ignore,
+    Implied,
+    include_bytes,
+    include_str,
+    include,
+    index_mut,
+    index,
+    Index,
+    into_future,
+    into_iter,
+    IntoFuture,
+    IntoIter,
+    IntoIterator,
+    is_empty,
+    Is,
+    isize,
+    Item,
+    iter_mut,
+    iter,
+    Iterator,
+    keyword,
+    lang,
+    le,
+    Left,
+    len,
+    line,
+    llvm_asm,
+    local_inner_macros,
+    log_syntax,
+    lt,
+    macro_export,
+    macro_rules,
+    macro_use,
+    main,
+    manually_drop,
+    may_dangle,
+    maybe_uninit,
+    metadata_type,
+    min_exhaustive_patterns,
+    miri,
+    missing,
+    module_path,
+    mul_assign,
+    mul,
+    ne,
+    neg,
+    Neg,
+    new_binary,
+    new_debug,
+    new_display,
+    new_lower_exp,
+    new_lower_hex,
+    new_octal,
+    new_pointer,
+    new_unchecked,
+    new_upper_exp,
+    new_upper_hex,
+    new_v1_formatted,
+    new,
+    next,
+    no_core,
+    no_mangle,
+    no_std,
+    non_exhaustive,
+    none,
+    None,
+    not,
+    Not,
+    notable_trait,
+    Ok,
+    opaque,
+    ops,
+    option_env,
+    option,
+    Option,
+    Ord,
+    Output,
+    owned_box,
+    packed,
+    panic_2015,
+    panic_2021,
+    panic_bounds_check,
+    panic_cannot_unwind,
+    panic_display,
+    panic_fmt,
+    panic_impl,
+    panic_info,
+    panic_location,
+    panic_misaligned_pointer_dereference,
+    panic_nounwind,
+    panic,
+    Param,
+    partial_ord,
+    PartialEq,
+    PartialOrd,
+    path,
+    Pending,
+    phantom_data,
+    pieces,
+    pin,
+    pointee_trait,
+    pointer_like,
+    poll,
+    Poll,
+    prelude_import,
+    prelude,
+    proc_macro_attribute,
+    proc_macro_derive,
+    proc_macro,
+    quote,
+    range_inclusive_new,
+    Range,
+    RangeFrom,
+    RangeFull,
+    RangeInclusive,
+    RangeTo,
+    RangeToInclusive,
+    Ready,
+    receiver,
+    recursion_limit,
+    register_attr,
+    register_tool,
+    rem_assign,
+    rem,
+    repr,
+    result,
+    Result,
+    ResumeTy,
+    Right,
+    rust_2015,
+    rust_2018,
+    rust_2021,
+    rust_2024,
+    rust_analyzer,
+    Rust,
+    rustc_allow_incoherent_impl,
+    rustc_builtin_macro,
+    rustc_coherence_is_core,
+    rustc_const_panic_str,
+    rustc_deprecated_safe_2024,
+    rustc_has_incoherent_inherent_impls,
+    rustc_layout_scalar_valid_range_end,
+    rustc_layout_scalar_valid_range_start,
+    rustc_legacy_const_generics,
+    rustc_macro_transparency,
+    rustc_reservation_impl,
+    rustc_safe_intrinsic,
+    rustc_skip_array_during_method_dispatch,
+    rustc_skip_during_method_dispatch,
+    semitransparent,
+    shl_assign,
+    shl,
+    shr_assign,
+    shr,
+    simd,
+    sized,
+    slice_len_fn,
+    Some,
+    start,
+    std_panic,
+    std,
+    stdcall,
+    str,
+    string,
+    String,
+    stringify,
+    structural_peq,
+    structural_teq,
+    sub_assign,
+    sub,
+    sync,
+    system,
+    sysv64,
+    Target,
+    termination,
+    test_case,
+    test,
+    thiscall,
+    trace_macros,
+    transmute_opts,
+    transmute_trait,
+    transparent,
+    Try,
+    tuple_trait,
+    u128,
+    u16,
+    u32,
+    u64,
+    u8,
+    unadjusted,
+    Unknown,
+    unpin,
+    unreachable_2015,
+    unreachable_2021,
+    unreachable,
+    unsafe_cell,
+    unsize,
+    unstable,
+    usize,
+    v1,
+    va_list,
+    vectorcall,
+    wasm,
+    win64,
+    array,
+    boxed_slice,
+}
diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml
index b6f90ec53b8bc..64ed93bbb1640 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml
@@ -27,6 +27,7 @@ span.workspace = true
 tt.workspace = true
 vfs-notify.workspace = true
 vfs.workspace = true
+intern.workspace = true
 
 [features]
 in-rust-tree = ["hir-expand/in-rust-tree"]
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 de68b867145dc..8737f2246be59 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -15,9 +15,10 @@ use ide_db::{
 };
 use itertools::Itertools;
 use proc_macro_api::{MacroDylib, ProcMacroServer};
-use project_model::{CargoConfig, ManifestPath, PackageRoot, ProjectManifest, ProjectWorkspace};
+use project_model::{
+    CargoConfig, ManifestPath, PackageRoot, ProjectManifest, ProjectWorkspace, ProjectWorkspaceKind,
+};
 use span::Span;
-use tracing::instrument;
 use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath};
 
 pub struct LoadCargoConfig {
@@ -51,7 +52,6 @@ pub fn load_workspace_at(
     load_workspace(workspace, &cargo_config.extra_env, load_config)
 }
 
-#[instrument(skip_all)]
 pub fn load_workspace(
     ws: ProjectWorkspace,
     extra_env: &FxHashMap,
@@ -68,11 +68,14 @@ pub fn load_workspace(
     let proc_macro_server = match &load_config.with_proc_macro_server {
         ProcMacroServerChoice::Sysroot => ws
             .find_sysroot_proc_macro_srv()
-            .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into)),
+            .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into))
+            .map_err(|e| (e, true)),
         ProcMacroServerChoice::Explicit(path) => {
-            ProcMacroServer::spawn(path, extra_env).map_err(Into::into)
+            ProcMacroServer::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true))
+        }
+        ProcMacroServerChoice::None => {
+            Err((anyhow::format_err!("proc macro server disabled"), false))
         }
-        ProcMacroServerChoice::None => Err(anyhow::format_err!("proc macro server disabled")),
     };
 
     let (crate_graph, proc_macros) = ws.to_crate_graph(
@@ -87,7 +90,7 @@ pub fn load_workspace(
     let proc_macros = {
         let proc_macro_server = match &proc_macro_server {
             Ok(it) => Ok(it),
-            Err(e) => Err(e.to_string()),
+            Err((e, hard_err)) => Err((e.to_string(), *hard_err)),
         };
         proc_macros
             .into_iter()
@@ -95,7 +98,7 @@ pub fn load_workspace(
                 (
                     crate_id,
                     path.map_or_else(
-                        |_| Err("proc macro crate is missing dylib".to_owned()),
+                        |e| Err((e, true)),
                         |(_, path)| {
                             proc_macro_server.as_ref().map_err(Clone::clone).and_then(
                                 |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
@@ -240,15 +243,34 @@ impl ProjectFolders {
 
         // register the workspace manifest as well, note that this currently causes duplicates for
         // non-virtual cargo workspaces! We ought to fix that
-        for manifest in workspaces.iter().filter_map(|ws| ws.manifest().map(ManifestPath::as_ref)) {
-            let file_set_roots: Vec = vec![VfsPath::from(manifest.to_owned())];
+        for ws in workspaces.iter() {
+            let mut file_set_roots: Vec = vec![];
+            let mut entries = vec![];
 
-            let entry = vfs::loader::Entry::Files(vec![manifest.to_owned()]);
+            if let Some(manifest) = ws.manifest().map(ManifestPath::as_ref) {
+                file_set_roots.push(VfsPath::from(manifest.to_owned()));
+                entries.push(manifest.to_owned());
+            }
 
-            res.watch.push(res.load.len());
-            res.load.push(entry);
-            local_filesets.push(fsc.len() as u64);
-            fsc.add_file_set(file_set_roots)
+            // In case of detached files we do **not** look for a rust-analyzer.toml.
+            if !matches!(ws.kind, ProjectWorkspaceKind::DetachedFile { .. }) {
+                let ws_root = ws.workspace_root();
+                let ratoml_path = {
+                    let mut p = ws_root.to_path_buf();
+                    p.push("rust-analyzer.toml");
+                    p
+                };
+                file_set_roots.push(VfsPath::from(ratoml_path.to_owned()));
+                entries.push(ratoml_path.to_owned());
+            }
+
+            if !file_set_roots.is_empty() {
+                let entry = vfs::loader::Entry::Files(entries);
+                res.watch.push(res.load.len());
+                res.load.push(entry);
+                local_filesets.push(fsc.len() as u64);
+                fsc.add_file_set(file_set_roots)
+            }
         }
 
         let fsc = fsc.build();
@@ -336,8 +358,7 @@ impl SourceRootConfig {
     }
 }
 
-/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
-/// with an identity dummy expander.
+/// Load the proc-macros for the given lib path, disabling all expanders whose names are in `ignored_macros`.
 pub fn load_proc_macro(
     server: &ProcMacroServer,
     path: &AbsPath,
@@ -364,7 +385,7 @@ pub fn load_proc_macro(
         }
         Err(e) => {
             tracing::warn!("proc-macro loading for {path} failed: {e}");
-            Err(e)
+            Err((e, true))
         }
     }
 }
@@ -379,7 +400,7 @@ fn load_crate_graph(
 ) -> RootDatabase {
     let ProjectWorkspace { toolchain, target_layout, .. } = ws;
 
-    let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok());
+    let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok());
     let mut db = RootDatabase::new(lru_cap);
     let mut analysis_change = ChangeWithProcMacros::new();
 
@@ -428,14 +449,19 @@ fn expander_to_proc_macro(
     expander: proc_macro_api::ProcMacro,
     ignored_macros: &[Box],
 ) -> ProcMacro {
-    let name = From::from(expander.name());
+    let name = expander.name();
     let kind = match expander.kind() {
         proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive,
         proc_macro_api::ProcMacroKind::Bang => ProcMacroKind::Bang,
         proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
     };
-    let disabled = ignored_macros.iter().any(|replace| **replace == name);
-    ProcMacro { name, kind, expander: sync::Arc::new(Expander(expander)), disabled }
+    let disabled = ignored_macros.iter().any(|replace| **replace == *name);
+    ProcMacro {
+        name: intern::Symbol::intern(name),
+        kind,
+        expander: sync::Arc::new(Expander(expander)),
+        disabled,
+    }
 }
 
 #[derive(Debug)]
diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml
index 18444018e1b68..57834623e84fa 100644
--- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml
@@ -24,12 +24,13 @@ parser.workspace = true
 tt.workspace = true
 stdx.workspace = true
 span.workspace = true
+intern.workspace = true
 
 [dev-dependencies]
 test-utils.workspace = true
 
 [features]
-in-rust-tree = ["parser/in-rust-tree", "syntax/in-rust-tree"]
+in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"]
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index 27dbc84a2b1aa..b6db4d2e76c40 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -1,10 +1,11 @@
 //! This module add real world mbe example for benchmark tests
 
+use intern::Symbol;
 use rustc_hash::FxHashMap;
 use span::{Edition, Span};
 use syntax::{
     ast::{self, HasName},
-    AstNode, SmolStr,
+    AstNode,
 };
 use test_utils::{bench, bench_fixture, skip_slow_tests};
 
@@ -24,9 +25,7 @@ fn benchmark_parse_macro_rules() {
         rules
             .values()
             .map(|it| {
-                DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT, true)
-                    .rules
-                    .len()
+                DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT).rules.len()
             })
             .sum()
     };
@@ -46,7 +45,7 @@ fn benchmark_expand_macro_rules() {
         invocations
             .into_iter()
             .map(|(id, tt)| {
-                let res = rules[&id].expand(&tt, |_| (), true, DUMMY, Edition::CURRENT);
+                let res = rules[&id].expand(&tt, |_| (), DUMMY, Edition::CURRENT);
                 assert!(res.err.is_none());
                 res.value.0.token_trees.len()
             })
@@ -58,9 +57,7 @@ fn benchmark_expand_macro_rules() {
 fn macro_rules_fixtures() -> FxHashMap {
     macro_rules_fixtures_tt()
         .into_iter()
-        .map(|(id, tt)| {
-            (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT, true))
-        })
+        .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT)))
         .collect()
 }
 
@@ -121,7 +118,7 @@ fn invocation_fixtures(
                         },
                         token_trees: token_trees.into_boxed_slice(),
                     };
-                    if it.expand(&subtree, |_| (), true, DUMMY, Edition::CURRENT).err.is_none() {
+                    if it.expand(&subtree, |_| (), DUMMY, Edition::CURRENT).err.is_none() {
                         res.push((name.clone(), subtree));
                         break;
                     }
@@ -226,13 +223,24 @@ fn invocation_fixtures(
             *seed
         }
         fn make_ident(ident: &str) -> tt::TokenTree {
-            tt::Leaf::Ident(tt::Ident { span: DUMMY, text: SmolStr::new(ident) }).into()
+            tt::Leaf::Ident(tt::Ident {
+                span: DUMMY,
+                sym: Symbol::intern(ident),
+                is_raw: tt::IdentIsRaw::No,
+            })
+            .into()
         }
         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 {
-            tt::Leaf::Literal(tt::Literal { span: DUMMY, text: SmolStr::new(lit) }).into()
+            tt::Leaf::Literal(tt::Literal {
+                span: DUMMY,
+                symbol: Symbol::intern(lit),
+                kind: tt::LitKind::Str,
+                suffix: None,
+            })
+            .into()
         }
         fn make_subtree(
             kind: tt::DelimiterKind,
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
index cfad8bcc0b454..1979e5171ab0c 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
@@ -5,17 +5,16 @@
 mod matcher;
 mod transcriber;
 
+use intern::Symbol;
 use rustc_hash::FxHashMap;
 use span::{Edition, Span};
-use syntax::SmolStr;
 
-use crate::{parser::MetaVarKind, ExpandError, ExpandResult, MatchedArmIndex};
+use crate::{parser::MetaVarKind, ExpandError, ExpandErrorKind, ExpandResult, MatchedArmIndex};
 
 pub(crate) fn expand_rules(
     rules: &[crate::Rule],
     input: &tt::Subtree,
     marker: impl Fn(&mut Span) + Copy,
-    new_meta_vars: bool,
     call_site: Span,
     def_site_edition: Edition,
 ) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> {
@@ -27,13 +26,8 @@ pub(crate) fn expand_rules(
             // If we find a rule that applies without errors, we're done.
             // Unconditionally returning the transcription here makes the
             // `test_repeat_bad_var` test fail.
-            let ExpandResult { value, err: transcribe_err } = transcriber::transcribe(
-                &rule.rhs,
-                &new_match.bindings,
-                marker,
-                new_meta_vars,
-                call_site,
-            );
+            let ExpandResult { value, err: transcribe_err } =
+                transcriber::transcribe(&rule.rhs, &new_match.bindings, marker, call_site);
             if transcribe_err.is_none() {
                 return ExpandResult::ok((value, Some(idx as u32)));
             }
@@ -52,7 +46,7 @@ pub(crate) fn expand_rules(
     if let Some((match_, rule, idx)) = match_ {
         // if we got here, there was no match without errors
         let ExpandResult { value, err: transcribe_err } =
-            transcriber::transcribe(&rule.rhs, &match_.bindings, marker, new_meta_vars, call_site);
+            transcriber::transcribe(&rule.rhs, &match_.bindings, marker, call_site);
         ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) }
     } else {
         ExpandResult::new(
@@ -63,7 +57,7 @@ pub(crate) fn expand_rules(
                 },
                 None,
             ),
-            ExpandError::NoMatchingRule,
+            ExpandError::new(call_site, ExpandErrorKind::NoMatchingRule),
         )
     }
 }
@@ -110,12 +104,12 @@ pub(crate) fn expand_rules(
 /// the `Bindings` we should take. We push to the stack when we enter a
 /// repetition.
 ///
-/// In other words, `Bindings` is a *multi* mapping from `SmolStr` to
+/// In other words, `Bindings` is a *multi* mapping from `Symbol` to
 /// `tt::TokenTree`, where the index to select a particular `TokenTree` among
 /// many is not a plain `usize`, but a `&[usize]`.
 #[derive(Debug, Default, Clone, PartialEq, Eq)]
 struct Bindings {
-    inner: FxHashMap,
+    inner: FxHashMap,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index b20d5579ca63b..e69d7d14e253a 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -61,25 +61,25 @@
 
 use std::{rc::Rc, sync::Arc};
 
+use intern::{sym, Symbol};
 use smallvec::{smallvec, SmallVec};
 use span::{Edition, Span};
-use syntax::SmolStr;
 use tt::{iter::TtIter, DelimSpan};
 
 use crate::{
     expander::{Binding, Bindings, ExpandResult, Fragment},
     expect_fragment,
     parser::{MetaVarKind, Op, RepeatKind, Separator},
-    ExpandError, MetaTemplate, ValueResult,
+    ExpandError, ExpandErrorKind, MetaTemplate, ValueResult,
 };
 
 impl Bindings {
-    fn push_optional(&mut self, name: &SmolStr) {
-        self.inner.insert(name.clone(), Binding::Fragment(Fragment::Empty));
+    fn push_optional(&mut self, name: Symbol) {
+        self.inner.insert(name, Binding::Fragment(Fragment::Empty));
     }
 
-    fn push_empty(&mut self, name: &SmolStr) {
-        self.inner.insert(name.clone(), Binding::Empty);
+    fn push_empty(&mut self, name: Symbol) {
+        self.inner.insert(name, Binding::Empty);
     }
 
     fn bindings(&self) -> impl Iterator {
@@ -127,10 +127,10 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, edition:
 
 #[derive(Debug, Clone)]
 enum BindingKind {
-    Empty(SmolStr),
-    Optional(SmolStr),
-    Fragment(SmolStr, Fragment),
-    Missing(SmolStr, MetaVarKind),
+    Empty(Symbol),
+    Optional(Symbol),
+    Fragment(Symbol, Fragment),
+    Missing(Symbol, MetaVarKind),
     Nested(usize, usize),
 }
 
@@ -178,20 +178,20 @@ impl BindingsBuilder {
         }
     }
 
-    fn push_empty(&mut self, idx: &mut BindingsIdx, var: &SmolStr) {
+    fn push_empty(&mut self, idx: &mut BindingsIdx, var: &Symbol) {
         self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Empty(var.clone()))));
     }
 
-    fn push_optional(&mut self, idx: &mut BindingsIdx, var: &SmolStr) {
+    fn push_optional(&mut self, idx: &mut BindingsIdx, var: &Symbol) {
         self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone()))));
     }
 
-    fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) {
+    fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment) {
         self.nodes[idx.0]
             .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment))));
     }
 
-    fn push_missing(&mut self, idx: &mut BindingsIdx, var: &SmolStr, kind: MetaVarKind) {
+    fn push_missing(&mut self, idx: &mut BindingsIdx, var: &Symbol, kind: MetaVarKind) {
         self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Missing(var.clone(), kind))));
     }
 
@@ -219,10 +219,10 @@ impl BindingsBuilder {
         for cmd in nodes {
             match cmd {
                 BindingKind::Empty(name) => {
-                    bindings.push_empty(name);
+                    bindings.push_empty(name.clone());
                 }
                 BindingKind::Optional(name) => {
-                    bindings.push_optional(name);
+                    bindings.push_optional(name.clone());
                 }
                 BindingKind::Fragment(name, fragment) => {
                     bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone()));
@@ -507,28 +507,40 @@ fn match_loop_inner<'t>(
             }
             OpDelimited::Op(Op::Literal(lhs)) => {
                 if let Ok(rhs) = src.clone().expect_leaf() {
-                    if matches!(rhs, tt::Leaf::Literal(it) if it.text == lhs.text) {
+                    if matches!(rhs, tt::Leaf::Literal(it) if it.symbol == lhs.symbol) {
                         item.dot.next();
                     } else {
-                        res.add_err(ExpandError::UnexpectedToken);
+                        res.add_err(ExpandError::new(
+                            *rhs.span(),
+                            ExpandErrorKind::UnexpectedToken,
+                        ));
                         item.is_error = true;
                     }
                 } else {
-                    res.add_err(ExpandError::binding_error(format!("expected literal: `{lhs}`")));
+                    res.add_err(ExpandError::binding_error(
+                        src.clone().next().map_or(delim_span.close, |it| it.first_span()),
+                        format!("expected literal: `{lhs}`"),
+                    ));
                     item.is_error = true;
                 }
                 try_push!(next_items, item);
             }
             OpDelimited::Op(Op::Ident(lhs)) => {
                 if let Ok(rhs) = src.clone().expect_leaf() {
-                    if matches!(rhs, tt::Leaf::Ident(it) if it.text == lhs.text) {
+                    if matches!(rhs, tt::Leaf::Ident(it) if it.sym == lhs.sym) {
                         item.dot.next();
                     } else {
-                        res.add_err(ExpandError::UnexpectedToken);
+                        res.add_err(ExpandError::new(
+                            *rhs.span(),
+                            ExpandErrorKind::UnexpectedToken,
+                        ));
                         item.is_error = true;
                     }
                 } else {
-                    res.add_err(ExpandError::binding_error(format!("expected ident: `{lhs}`")));
+                    res.add_err(ExpandError::binding_error(
+                        src.clone().next().map_or(delim_span.close, |it| it.first_span()),
+                        format!("expected ident: `{lhs}`"),
+                    ));
                     item.is_error = true;
                 }
                 try_push!(next_items, item);
@@ -538,8 +550,8 @@ fn match_loop_inner<'t>(
                 let error = if let Ok(rhs) = fork.expect_glued_punct() {
                     let first_is_single_quote = rhs[0].char == '\'';
                     let lhs = lhs.iter().map(|it| it.char);
-                    let rhs = rhs.iter().map(|it| it.char);
-                    if lhs.clone().eq(rhs) {
+                    let rhs_ = rhs.iter().map(|it| it.char);
+                    if lhs.clone().eq(rhs_) {
                         // HACK: here we use `meta_result` to pass `TtIter` back to caller because
                         // it might have been advanced multiple times. `ValueResult` is
                         // insignificant.
@@ -552,13 +564,19 @@ fn match_loop_inner<'t>(
                     if first_is_single_quote {
                         // If the first punct token is a single quote, that's a part of a lifetime
                         // ident, not a punct.
-                        ExpandError::UnexpectedToken
+                        ExpandError::new(
+                            rhs.get(1).map_or(rhs[0].span, |it| it.span),
+                            ExpandErrorKind::UnexpectedToken,
+                        )
                     } else {
-                        let lhs: SmolStr = lhs.collect();
-                        ExpandError::binding_error(format!("expected punct: `{lhs}`"))
+                        let lhs = lhs.collect::();
+                        ExpandError::binding_error(rhs[0].span, format!("expected punct: `{lhs}`"))
                     }
                 } else {
-                    ExpandError::UnexpectedToken
+                    ExpandError::new(
+                        src.clone().next().map_or(delim_span.close, |it| it.first_span()),
+                        ExpandErrorKind::UnexpectedToken,
+                    )
                 };
 
                 res.add_err(error);
@@ -651,7 +669,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition)
                 if let Some(item) = error_recover_item {
                     res.bindings = bindings_builder.build(&item);
                 }
-                res.add_err(ExpandError::UnexpectedToken);
+                res.add_err(ExpandError::new(span.open, ExpandErrorKind::UnexpectedToken));
             }
             return res;
         }
@@ -670,7 +688,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition)
                 src = it;
                 res.unmatched_tts += src.len();
             }
-            res.add_err(ExpandError::LeftoverTokens);
+            res.add_err(ExpandError::new(span.open, ExpandErrorKind::LeftoverTokens));
 
             if let Some(error_recover_item) = error_recover_item {
                 res.bindings = bindings_builder.build(&error_recover_item);
@@ -746,9 +764,10 @@ fn match_meta_var(
 ) -> ExpandResult> {
     let fragment = match kind {
         MetaVarKind::Path => {
-            return expect_fragment(input, parser::PrefixEntryPoint::Path, edition).map(|it| {
-                it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path)
-            });
+            return expect_fragment(input, parser::PrefixEntryPoint::Path, edition, delim_span)
+                .map(|it| {
+                    it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path)
+                });
         }
         MetaVarKind::Expr => {
             // `expr` should not match underscores, let expressions, or inline const. The latter
@@ -759,39 +778,58 @@ fn match_meta_var(
             // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576
             match input.peek_n(0) {
                 Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it)))
-                    if it.text == "_" || it.text == "let" || it.text == "const" =>
+                    if it.sym == sym::underscore
+                        || it.sym == sym::let_
+                        || it.sym == sym::const_ =>
                 {
-                    return ExpandResult::only_err(ExpandError::NoMatchingRule)
+                    return ExpandResult::only_err(ExpandError::new(
+                        it.span,
+                        ExpandErrorKind::NoMatchingRule,
+                    ))
                 }
                 _ => {}
             };
-            return expect_fragment(input, parser::PrefixEntryPoint::Expr, edition).map(|tt| {
-                tt.map(|tt| match tt {
-                    tt::TokenTree::Leaf(leaf) => tt::Subtree {
-                        delimiter: tt::Delimiter::invisible_spanned(*leaf.span()),
-                        token_trees: Box::new([leaf.into()]),
-                    },
-                    tt::TokenTree::Subtree(mut s) => {
-                        if s.delimiter.kind == tt::DelimiterKind::Invisible {
-                            s.delimiter.kind = tt::DelimiterKind::Parenthesis;
+            return expect_fragment(input, parser::PrefixEntryPoint::Expr, edition, delim_span)
+                .map(|tt| {
+                    tt.map(|tt| match tt {
+                        tt::TokenTree::Leaf(leaf) => tt::Subtree {
+                            delimiter: tt::Delimiter::invisible_spanned(*leaf.span()),
+                            token_trees: Box::new([leaf.into()]),
+                        },
+                        tt::TokenTree::Subtree(mut s) => {
+                            if s.delimiter.kind == tt::DelimiterKind::Invisible {
+                                s.delimiter.kind = tt::DelimiterKind::Parenthesis;
+                            }
+                            s
                         }
-                        s
-                    }
-                })
-                .map(Fragment::Expr)
-            });
+                    })
+                    .map(Fragment::Expr)
+                });
         }
         MetaVarKind::Ident | MetaVarKind::Tt | MetaVarKind::Lifetime | MetaVarKind::Literal => {
+            let span = input.next_span();
             let tt_result = match kind {
                 MetaVarKind::Ident => input
                     .expect_ident()
                     .map(|ident| tt::Leaf::from(ident.clone()).into())
-                    .map_err(|()| ExpandError::binding_error("expected ident")),
-                MetaVarKind::Tt => {
-                    expect_tt(input).map_err(|()| ExpandError::binding_error("expected token tree"))
-                }
-                MetaVarKind::Lifetime => expect_lifetime(input)
-                    .map_err(|()| ExpandError::binding_error("expected lifetime")),
+                    .map_err(|()| {
+                        ExpandError::binding_error(
+                            span.unwrap_or(delim_span.close),
+                            "expected ident",
+                        )
+                    }),
+                MetaVarKind::Tt => expect_tt(input).map_err(|()| {
+                    ExpandError::binding_error(
+                        span.unwrap_or(delim_span.close),
+                        "expected token tree",
+                    )
+                }),
+                MetaVarKind::Lifetime => expect_lifetime(input).map_err(|()| {
+                    ExpandError::binding_error(
+                        span.unwrap_or(delim_span.close),
+                        "expected lifetime",
+                    )
+                }),
                 MetaVarKind::Literal => {
                     let neg = eat_char(input, '-');
                     input
@@ -806,7 +844,12 @@ fn match_meta_var(
                                 }),
                             }
                         })
-                        .map_err(|()| ExpandError::binding_error("expected literal"))
+                        .map_err(|()| {
+                            ExpandError::binding_error(
+                                span.unwrap_or(delim_span.close),
+                                "expected literal",
+                            )
+                        })
                 }
                 _ => unreachable!(),
             };
@@ -821,10 +864,10 @@ fn match_meta_var(
         MetaVarKind::Item => parser::PrefixEntryPoint::Item,
         MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
     };
-    expect_fragment(input, fragment, edition).map(|it| it.map(Fragment::Tokens))
+    expect_fragment(input, fragment, edition, delim_span).map(|it| it.map(Fragment::Tokens))
 }
 
-fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) {
+fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) {
     for op in pattern.iter() {
         match op {
             Op::Var { name, .. } => collector_fun(name.clone()),
@@ -908,13 +951,13 @@ fn expect_separator(iter: &mut TtIter<'_, S>, separator: &Separator) ->
     let mut fork = iter.clone();
     let ok = match separator {
         Separator::Ident(lhs) => match fork.expect_ident_or_underscore() {
-            Ok(rhs) => rhs.text == lhs.text,
+            Ok(rhs) => rhs.sym == lhs.sym,
             Err(_) => false,
         },
         Separator::Literal(lhs) => match fork.expect_literal() {
             Ok(rhs) => match rhs {
-                tt::Leaf::Literal(rhs) => rhs.text == lhs.text,
-                tt::Leaf::Ident(rhs) => rhs.text == lhs.text,
+                tt::Leaf::Literal(rhs) => rhs.symbol == lhs.symbol,
+                tt::Leaf::Ident(rhs) => rhs.sym == lhs.symbol,
                 tt::Leaf::Punct(_) => false,
             },
             Err(_) => false,
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index c09cbd1d071ea..286bd748cbe7e 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
@@ -1,36 +1,39 @@
 //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like
 //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}`
 
+use intern::{sym, Symbol};
 use span::Span;
-use syntax::SmolStr;
 use tt::Delimiter;
 
 use crate::{
     expander::{Binding, Bindings, Fragment},
     parser::{MetaVarKind, Op, RepeatKind, Separator},
-    CountError, ExpandError, ExpandResult, MetaTemplate,
+    ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate,
 };
 
 impl Bindings {
-    fn get(&self, name: &str) -> Result<&Binding, ExpandError> {
+    fn get(&self, name: &Symbol, span: Span) -> Result<&Binding, ExpandError> {
         match self.inner.get(name) {
             Some(binding) => Ok(binding),
-            None => Err(ExpandError::UnresolvedBinding(Box::new(Box::from(name)))),
+            None => Err(ExpandError::new(
+                span,
+                ExpandErrorKind::UnresolvedBinding(Box::new(Box::from(name.as_str()))),
+            )),
         }
     }
 
     fn get_fragment(
         &self,
-        name: &str,
+        name: &Symbol,
         mut span: Span,
         nesting: &mut [NestingState],
         marker: impl Fn(&mut Span),
     ) -> Result {
         macro_rules! binding_err {
-            ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
+            ($($arg:tt)*) => { ExpandError::binding_error(span, format!($($arg)*)) };
         }
 
-        let mut b = self.get(name)?;
+        let mut b = self.get(name, span)?;
         for nesting_state in nesting.iter_mut() {
             nesting_state.hit = true;
             b = match b {
@@ -97,8 +100,9 @@ impl Bindings {
                     | MetaVarKind::Expr
                     | MetaVarKind::Ident => {
                         Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                            text: SmolStr::new_static("missing"),
+                            sym: sym::missing.clone(),
                             span,
+                            is_raw: tt::IdentIsRaw::No,
                         })))
                     }
                     MetaVarKind::Lifetime => {
@@ -111,16 +115,18 @@ impl Bindings {
                                     spacing: tt::Spacing::Joint,
                                 })),
                                 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                                    text: SmolStr::new_static("missing"),
+                                    sym: sym::missing.clone(),
                                     span,
+                                    is_raw: tt::IdentIsRaw::No,
                                 })),
                             ]),
                         }))
                     }
                     MetaVarKind::Literal => {
                         Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                            text: SmolStr::new_static("\"missing\""),
+                            sym: sym::missing.clone(),
                             span,
+                            is_raw: tt::IdentIsRaw::No,
                         })))
                     }
                 }
@@ -139,10 +145,9 @@ pub(super) fn transcribe(
     template: &MetaTemplate,
     bindings: &Bindings,
     marker: impl Fn(&mut Span) + Copy,
-    new_meta_vars: bool,
     call_site: Span,
 ) -> ExpandResult> {
-    let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), new_meta_vars, call_site };
+    let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), call_site };
     let mut arena: Vec> = Vec::new();
     expand_subtree(&mut ctx, template, None, &mut arena, marker)
 }
@@ -162,7 +167,6 @@ struct NestingState {
 struct ExpandCtx<'a> {
     bindings: &'a Bindings,
     nesting: Vec,
-    new_meta_vars: bool,
     call_site: Span,
 }
 
@@ -236,8 +240,10 @@ fn expand_subtree(
                     ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx);
                 arena.push(
                     tt::Leaf::Literal(tt::Literal {
-                        text: index.to_string().into(),
+                        symbol: Symbol::integer(index),
                         span: ctx.call_site,
+                        kind: tt::LitKind::Integer,
+                        suffix: None,
                     })
                     .into(),
                 );
@@ -249,14 +255,16 @@ fn expand_subtree(
                 });
                 arena.push(
                     tt::Leaf::Literal(tt::Literal {
-                        text: length.to_string().into(),
+                        symbol: Symbol::integer(length),
                         span: ctx.call_site,
+                        kind: tt::LitKind::Integer,
+                        suffix: None,
                     })
                     .into(),
                 );
             }
             Op::Count { name, depth } => {
-                let mut binding = match ctx.bindings.get(name.as_str()) {
+                let mut binding = match ctx.bindings.get(name, ctx.call_site) {
                     Ok(b) => b,
                     Err(e) => {
                         if err.is_none() {
@@ -292,30 +300,14 @@ fn expand_subtree(
                     }
                 }
 
-                let res = if ctx.new_meta_vars {
-                    count(binding, 0, depth.unwrap_or(0))
-                } else {
-                    count_old(binding, 0, *depth)
-                };
+                let res = count(binding, 0, depth.unwrap_or(0));
 
-                let c = match res {
-                    Ok(c) => c,
-                    Err(e) => {
-                        // XXX: It *might* make sense to emit a dummy integer value like `0` here.
-                        // That would type inference a bit more robust in cases like
-                        // `v[${count(t)}]` where index doesn't matter, but also could lead to
-                        // wrong infefrence for cases like `tup.${count(t)}` where index itself
-                        // does matter.
-                        if err.is_none() {
-                            err = Some(e.into());
-                        }
-                        continue;
-                    }
-                };
                 arena.push(
                     tt::Leaf::Literal(tt::Literal {
-                        text: c.to_string().into(),
+                        symbol: Symbol::integer(res),
                         span: ctx.call_site,
+                        suffix: None,
+                        kind: tt::LitKind::Integer,
                     })
                     .into(),
                 );
@@ -335,16 +327,16 @@ fn expand_subtree(
 
 fn expand_var(
     ctx: &mut ExpandCtx<'_>,
-    v: &SmolStr,
+    v: &Symbol,
     id: Span,
     marker: impl Fn(&mut Span),
 ) -> ExpandResult {
     // We already handle $crate case in mbe parser
-    debug_assert!(v != "crate");
+    debug_assert!(*v != sym::crate_);
 
     match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) {
         Ok(it) => ExpandResult::ok(it),
-        Err(ExpandError::UnresolvedBinding(_)) => {
+        Err(e) if matches!(e.inner.1, ExpandErrorKind::UnresolvedBinding(_)) => {
             // Note that it is possible to have a `$var` inside a macro which is not bound.
             // For example:
             // ```
@@ -363,7 +355,12 @@ fn expand_var(
                 token_trees: Box::new([
                     tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id })
                         .into(),
-                    tt::Leaf::from(tt::Ident { text: v.clone(), span: id }).into(),
+                    tt::Leaf::from(tt::Ident {
+                        sym: v.clone(),
+                        span: id,
+                        is_raw: tt::IdentIsRaw::No,
+                    })
+                    .into(),
                 ]),
             }
             .into();
@@ -421,7 +418,7 @@ fn expand_repeat(
                     }
                     .into(),
                 ),
-                err: Some(ExpandError::LimitExceeded),
+                err: Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded)),
             };
         }
 
@@ -467,16 +464,16 @@ fn expand_repeat(
     let tt = tt::Subtree {
         delimiter: tt::Delimiter::invisible_spanned(ctx.call_site),
         token_trees: buf.into_boxed_slice(),
-    }
-    .into();
+    };
 
     if RepeatKind::OneOrMore == kind && counter == 0 {
+        let span = tt.delimiter.open;
         return ExpandResult {
-            value: Fragment::Tokens(tt),
-            err: Some(ExpandError::UnexpectedToken),
+            value: Fragment::Tokens(tt.into()),
+            err: Some(ExpandError::new(span, ExpandErrorKind::UnexpectedToken)),
         };
     }
-    ExpandResult { value: Fragment::Tokens(tt), err }
+    ExpandResult { value: Fragment::Tokens(tt.into()), err }
 }
 
 fn push_fragment(ctx: &ExpandCtx<'_>, buf: &mut Vec>, fragment: Fragment) {
@@ -543,44 +540,16 @@ fn fix_up_and_push_path_tt(
 
 /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
 /// defined by the metavar expression.
-fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> Result {
+fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> usize {
     match binding {
         Binding::Nested(bs) => {
             if depth_curr == depth_max {
-                Ok(bs.len())
+                bs.len()
             } else {
                 bs.iter().map(|b| count(b, depth_curr + 1, depth_max)).sum()
             }
         }
-        Binding::Empty => Ok(0),
-        Binding::Fragment(_) | Binding::Missing(_) => Ok(1),
-    }
-}
-
-fn count_old(
-    binding: &Binding,
-    our_depth: usize,
-    count_depth: Option,
-) -> Result {
-    match binding {
-        Binding::Nested(bs) => match count_depth {
-            None => bs.iter().map(|b| count_old(b, our_depth + 1, None)).sum(),
-            Some(0) => Ok(bs.len()),
-            Some(d) => bs.iter().map(|b| count_old(b, our_depth + 1, Some(d - 1))).sum(),
-        },
-        Binding::Empty => Ok(0),
-        Binding::Fragment(_) | Binding::Missing(_) => {
-            if our_depth == 0 {
-                // `${count(t)}` is placed inside the innermost repetition. This includes cases
-                // where `t` is not a repeated fragment.
-                Err(CountError::Misplaced)
-            } else if count_depth.is_none() {
-                Ok(1)
-            } else {
-                // We've reached at the innermost repeated fragment, but the user wants us to go
-                // further!
-                Err(CountError::OutOfBounds)
-            }
-        }
+        Binding::Empty => 0,
+        Binding::Fragment(_) | Binding::Missing(_) => 1,
     }
 }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index b06c6cee12dbd..568490d57345f 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -15,10 +15,11 @@ mod to_parser_input;
 mod benchmark;
 
 use span::{Edition, Span, SyntaxContextId};
-use stdx::impl_from;
 use tt::iter::TtIter;
+use tt::DelimSpan;
 
 use std::fmt;
+use std::sync::Arc;
 
 use crate::parser::{MetaTemplate, MetaVarKind, Op};
 
@@ -27,9 +28,9 @@ pub use ::parser::TopEntryPoint;
 pub use tt::{Delimiter, DelimiterKind, Punct};
 
 pub use crate::syntax_bridge::{
-    parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span,
-    syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node,
-    DocCommentDesugarMode, SpanMapper,
+    desugar_doc_comment_text, parse_exprs_with_sep, parse_to_token_tree,
+    parse_to_token_tree_static_span, syntax_node_to_token_tree, syntax_node_to_token_tree_modified,
+    token_tree_to_syntax_node, DocCommentDesugarMode, SpanMapper,
 };
 
 pub use crate::syntax_bridge::dummy_test_span_utils::*;
@@ -64,39 +65,45 @@ impl fmt::Display for ParseError {
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
-pub enum ExpandError {
+pub struct ExpandError {
+    pub inner: Arc<(Span, ExpandErrorKind)>,
+}
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
+pub enum ExpandErrorKind {
     BindingError(Box>),
     UnresolvedBinding(Box>),
     LeftoverTokens,
-    ConversionError,
     LimitExceeded,
     NoMatchingRule,
     UnexpectedToken,
-    CountError(CountError),
 }
 
-impl_from!(CountError for ExpandError);
-
 impl ExpandError {
-    fn binding_error(e: impl Into>) -> ExpandError {
-        ExpandError::BindingError(Box::new(e.into()))
+    fn new(span: Span, kind: ExpandErrorKind) -> ExpandError {
+        ExpandError { inner: Arc::new((span, kind)) }
+    }
+    fn binding_error(span: Span, e: impl Into>) -> ExpandError {
+        ExpandError { inner: Arc::new((span, ExpandErrorKind::BindingError(Box::new(e.into())))) }
     }
 }
-
 impl fmt::Display for ExpandError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.inner.1.fmt(f)
+    }
+}
+
+impl fmt::Display for ExpandErrorKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            ExpandError::NoMatchingRule => f.write_str("no rule matches input tokens"),
-            ExpandError::UnexpectedToken => f.write_str("unexpected token in input"),
-            ExpandError::BindingError(e) => f.write_str(e),
-            ExpandError::UnresolvedBinding(binding) => {
+            ExpandErrorKind::NoMatchingRule => f.write_str("no rule matches input tokens"),
+            ExpandErrorKind::UnexpectedToken => f.write_str("unexpected token in input"),
+            ExpandErrorKind::BindingError(e) => f.write_str(e),
+            ExpandErrorKind::UnresolvedBinding(binding) => {
                 f.write_str("could not find binding ")?;
                 f.write_str(binding)
             }
-            ExpandError::ConversionError => f.write_str("could not convert tokens"),
-            ExpandError::LimitExceeded => f.write_str("Expand exceed limit"),
-            ExpandError::LeftoverTokens => f.write_str("leftover tokens"),
-            ExpandError::CountError(e) => e.fmt(f),
+            ExpandErrorKind::LimitExceeded => f.write_str("Expand exceed limit"),
+            ExpandErrorKind::LeftoverTokens => f.write_str("leftover tokens"),
         }
     }
 }
@@ -144,9 +151,7 @@ impl DeclarativeMacro {
     /// The old, `macro_rules! m {}` flavor.
     pub fn parse_macro_rules(
         tt: &tt::Subtree,
-        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
-        // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then)
-        new_meta_vars: bool,
+        ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition,
     ) -> DeclarativeMacro {
         // Note: this parsing can be implemented using mbe machinery itself, by
         // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
@@ -156,7 +161,7 @@ impl DeclarativeMacro {
         let mut err = None;
 
         while src.len() > 0 {
-            let rule = match Rule::parse(edition, &mut src, new_meta_vars) {
+            let rule = match Rule::parse(ctx_edition, &mut src) {
                 Ok(it) => it,
                 Err(e) => {
                     err = Some(Box::new(e));
@@ -186,9 +191,7 @@ impl DeclarativeMacro {
     pub fn parse_macro2(
         args: Option<&tt::Subtree>,
         body: &tt::Subtree,
-        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
-        // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then)
-        new_meta_vars: bool,
+        ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition,
     ) -> DeclarativeMacro {
         let mut rules = Vec::new();
         let mut err = None;
@@ -197,8 +200,8 @@ impl DeclarativeMacro {
             cov_mark::hit!(parse_macro_def_simple);
 
             let rule = (|| {
-                let lhs = MetaTemplate::parse_pattern(edition, args)?;
-                let rhs = MetaTemplate::parse_template(edition, body, new_meta_vars)?;
+                let lhs = MetaTemplate::parse_pattern(ctx_edition, args)?;
+                let rhs = MetaTemplate::parse_template(ctx_edition, body)?;
 
                 Ok(crate::Rule { lhs, rhs })
             })();
@@ -211,7 +214,7 @@ impl DeclarativeMacro {
             cov_mark::hit!(parse_macro_def_rules);
             let mut src = TtIter::new(body);
             while src.len() > 0 {
-                let rule = match Rule::parse(edition, &mut src, new_meta_vars) {
+                let rule = match Rule::parse(ctx_edition, &mut src) {
                     Ok(it) => it,
                     Err(e) => {
                         err = Some(Box::new(e));
@@ -252,11 +255,10 @@ impl DeclarativeMacro {
         &self,
         tt: &tt::Subtree,
         marker: impl Fn(&mut Span) + Copy,
-        new_meta_vars: bool,
         call_site: Span,
         def_site_edition: Edition,
     ) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> {
-        expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site, def_site_edition)
+        expander::expand_rules(&self.rules, tt, marker, call_site, def_site_edition)
     }
 }
 
@@ -264,7 +266,6 @@ impl Rule {
     fn parse(
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         src: &mut TtIter<'_, Span>,
-        new_meta_vars: bool,
     ) -> Result {
         let lhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
         src.expect_char('=').map_err(|()| ParseError::expected("expected `=`"))?;
@@ -272,7 +273,7 @@ impl Rule {
         let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
 
         let lhs = MetaTemplate::parse_pattern(edition, lhs)?;
-        let rhs = MetaTemplate::parse_template(edition, rhs, new_meta_vars)?;
+        let rhs = MetaTemplate::parse_template(edition, rhs)?;
 
         Ok(crate::Rule { lhs, rhs })
     }
@@ -360,14 +361,15 @@ impl From> for ValueResult {
     }
 }
 
-fn expect_fragment(
-    tt_iter: &mut TtIter<'_, S>,
+fn expect_fragment(
+    tt_iter: &mut TtIter<'_, Span>,
     entry_point: ::parser::PrefixEntryPoint,
     edition: ::parser::Edition,
-) -> ExpandResult>> {
+    delim_span: DelimSpan,
+) -> ExpandResult>> {
     use ::parser;
     let buffer = tt::buffer::TokenBuffer::from_tokens(tt_iter.as_slice());
-    let parser_input = to_parser_input::to_parser_input(&buffer);
+    let parser_input = to_parser_input::to_parser_input(edition, &buffer);
     let tree_traversal = entry_point.parse(&parser_input, edition);
     let mut cursor = buffer.begin();
     let mut error = false;
@@ -392,7 +394,10 @@ fn expect_fragment(
     }
 
     let err = if error || !cursor.is_root() {
-        Some(ExpandError::binding_error(format!("expected {entry_point:?}")))
+        Some(ExpandError::binding_error(
+            buffer.begin().token_tree().map_or(delim_span.close, |tt| tt.span()),
+            format!("expected {entry_point:?}"),
+        ))
     } else {
         None
     };
diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
index 5c499c06b1525..218c04640f12e 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
@@ -4,8 +4,8 @@
 use std::sync::Arc;
 
 use arrayvec::ArrayVec;
+use intern::{sym, Symbol};
 use span::{Edition, Span, SyntaxContextId};
-use syntax::SmolStr;
 use tt::iter::TtIter;
 
 use crate::ParseError;
@@ -31,15 +31,14 @@ impl MetaTemplate {
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         pattern: &tt::Subtree,
     ) -> Result {
-        MetaTemplate::parse(edition, pattern, Mode::Pattern, false)
+        MetaTemplate::parse(edition, pattern, Mode::Pattern)
     }
 
     pub(crate) fn parse_template(
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         template: &tt::Subtree,
-        new_meta_vars: bool,
     ) -> Result {
-        MetaTemplate::parse(edition, template, Mode::Template, new_meta_vars)
+        MetaTemplate::parse(edition, template, Mode::Template)
     }
 
     pub(crate) fn iter(&self) -> impl Iterator {
@@ -50,13 +49,12 @@ impl MetaTemplate {
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         tt: &tt::Subtree,
         mode: Mode,
-        new_meta_vars: bool,
     ) -> Result {
         let mut src = TtIter::new(tt);
 
         let mut res = Vec::new();
         while let Some(first) = src.peek_n(0) {
-            let op = next_op(edition, first, &mut src, mode, new_meta_vars)?;
+            let op = next_op(edition, first, &mut src, mode)?;
             res.push(op);
         }
 
@@ -67,12 +65,12 @@ impl MetaTemplate {
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum Op {
     Var {
-        name: SmolStr,
+        name: Symbol,
         kind: Option,
         id: Span,
     },
     Ignore {
-        name: SmolStr,
+        name: Symbol,
         id: Span,
     },
     Index {
@@ -82,7 +80,7 @@ pub(crate) enum Op {
         depth: usize,
     },
     Count {
-        name: SmolStr,
+        name: Symbol,
         // FIXME: `usize`` once we drop support for 1.76
         depth: Option,
     },
@@ -138,8 +136,8 @@ impl PartialEq for Separator {
         use Separator::*;
 
         match (self, other) {
-            (Ident(a), Ident(b)) => a.text == b.text,
-            (Literal(a), Literal(b)) => a.text == b.text,
+            (Ident(a), Ident(b)) => a.sym == b.sym,
+            (Literal(a), Literal(b)) => a.symbol == b.symbol,
             (Puncts(a), Puncts(b)) if a.len() == b.len() => {
                 let a_iter = a.iter().map(|a| a.char);
                 let b_iter = b.iter().map(|b| b.char);
@@ -161,7 +159,6 @@ fn next_op(
     first_peeked: &tt::TokenTree,
     src: &mut TtIter<'_, Span>,
     mode: Mode,
-    new_meta_vars: bool,
 ) -> Result {
     let res = match first_peeked {
         tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => {
@@ -181,14 +178,14 @@ fn next_op(
                 tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind {
                     tt::DelimiterKind::Parenthesis => {
                         let (separator, kind) = parse_repeat(src)?;
-                        let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?;
+                        let tokens = MetaTemplate::parse(edition, subtree, mode)?;
                         Op::Repeat { tokens, separator: separator.map(Arc::new), kind }
                     }
                     tt::DelimiterKind::Brace => match mode {
                         Mode::Template => {
-                            parse_metavar_expr(new_meta_vars, &mut TtIter::new(subtree)).map_err(
-                                |()| ParseError::unexpected("invalid metavariable expression"),
-                            )?
+                            parse_metavar_expr(&mut TtIter::new(subtree)).map_err(|()| {
+                                ParseError::unexpected("invalid metavariable expression")
+                            })?
                         }
                         Mode::Pattern => {
                             return Err(ParseError::unexpected(
@@ -203,19 +200,23 @@ fn next_op(
                     }
                 },
                 tt::TokenTree::Leaf(leaf) => match leaf {
-                    tt::Leaf::Ident(ident) if ident.text == "crate" => {
+                    tt::Leaf::Ident(ident) if ident.sym == sym::crate_ => {
                         // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
-                        Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span })
+                        Op::Ident(tt::Ident {
+                            sym: sym::dollar_crate.clone(),
+                            span: ident.span,
+                            is_raw: tt::IdentIsRaw::No,
+                        })
                     }
                     tt::Leaf::Ident(ident) => {
                         let kind = eat_fragment_kind(edition, src, mode)?;
-                        let name = ident.text.clone();
+                        let name = ident.sym.clone();
                         let id = ident.span;
                         Op::Var { name, kind, id }
                     }
                     tt::Leaf::Literal(lit) if is_boolean_literal(lit) => {
                         let kind = eat_fragment_kind(edition, src, mode)?;
-                        let name = lit.text.clone();
+                        let name = lit.symbol.clone();
                         let id = lit.span;
                         Op::Var { name, kind, id }
                     }
@@ -256,7 +257,7 @@ fn next_op(
 
         tt::TokenTree::Subtree(subtree) => {
             src.next().expect("first token already peeked");
-            let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?;
+            let tokens = MetaTemplate::parse(edition, subtree, mode)?;
             Op::Subtree { tokens, delimiter: subtree.delimiter }
         }
     };
@@ -273,7 +274,7 @@ fn eat_fragment_kind(
         let ident = src
             .expect_ident()
             .map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
-        let kind = match ident.text.as_str() {
+        let kind = match ident.sym.as_str() {
             "path" => MetaVarKind::Path,
             "ty" => MetaVarKind::Ty,
             "pat" => match edition(ident.span.ctx) {
@@ -299,7 +300,7 @@ fn eat_fragment_kind(
 }
 
 fn is_boolean_literal(lit: &tt::Literal) -> bool {
-    matches!(lit.text.as_str(), "true" | "false")
+    matches!(lit.symbol.as_str(), "true" | "false")
 }
 
 fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, RepeatKind), ParseError> {
@@ -339,7 +340,7 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, Repeat
     Err(ParseError::InvalidRepeat)
 }
 
-fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result {
+fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result {
     let func = src.expect_ident()?;
     let args = src.expect_subtree()?;
 
@@ -349,23 +350,19 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result
 
     let mut args = TtIter::new(args);
 
-    let op = match &*func.text {
-        "ignore" => {
-            if new_meta_vars {
-                args.expect_dollar()?;
-            }
+    let op = match &func.sym {
+        s if sym::ignore == *s => {
+            args.expect_dollar()?;
             let ident = args.expect_ident()?;
-            Op::Ignore { name: ident.text.clone(), id: ident.span }
+            Op::Ignore { name: ident.sym.clone(), id: ident.span }
         }
-        "index" => Op::Index { depth: parse_depth(&mut args)? },
-        "len" => Op::Len { depth: parse_depth(&mut args)? },
-        "count" => {
-            if new_meta_vars {
-                args.expect_dollar()?;
-            }
+        s if sym::index == *s => Op::Index { depth: parse_depth(&mut args)? },
+        s if sym::len == *s => Op::Len { depth: parse_depth(&mut args)? },
+        s if sym::count == *s => {
+            args.expect_dollar()?;
             let ident = args.expect_ident()?;
             let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
-            Op::Count { name: ident.text.clone(), depth }
+            Op::Count { name: ident.sym.clone(), depth }
         }
         _ => return Err(()),
     };
@@ -380,9 +377,11 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result
 fn parse_depth(src: &mut TtIter<'_, Span>) -> Result {
     if src.len() == 0 {
         Ok(0)
-    } else if let tt::Leaf::Literal(lit) = src.expect_literal()? {
+    } else if let tt::Leaf::Literal(tt::Literal { symbol: text, suffix: None, .. }) =
+        src.expect_literal()?
+    {
         // Suffixes are not allowed.
-        lit.text.parse().map_err(|_| ())
+        text.as_str().parse().map_err(|_| ())
     } else {
         Err(())
     }
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 73a04f00d9306..a29efdd4ef77e 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -2,18 +2,20 @@
 
 use std::fmt;
 
+use intern::Symbol;
 use rustc_hash::{FxHashMap, FxHashSet};
 use span::{Edition, SpanAnchor, SpanData, SpanMap};
-use stdx::{never, non_empty_vec::NonEmptyVec};
+use stdx::{format_to, never, non_empty_vec::NonEmptyVec};
 use syntax::{
     ast::{self, make::tokens::doc_comment},
-    AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind,
-    SyntaxKind::*,
+    format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement,
+    SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T,
 };
 use tt::{
     buffer::{Cursor, TokenBuffer},
     iter::TtIter,
+    token_to_literal,
 };
 
 use crate::to_parser_input::to_parser_input;
@@ -50,7 +52,10 @@ pub(crate) mod dummy_test_span_utils {
     pub const DUMMY: Span = Span {
         range: TextRange::empty(TextSize::new(0)),
         anchor: span::SpanAnchor {
-            file_id: span::FileId::from_raw(0xe4e4e),
+            file_id: span::EditionedFileId::new(
+                span::FileId::from_raw(0xe4e4e),
+                span::Edition::CURRENT,
+            ),
             ast_id: span::ROOT_ERASED_FILE_AST_ID,
         },
         ctx: SyntaxContextId::ROOT,
@@ -63,7 +68,10 @@ pub(crate) mod dummy_test_span_utils {
             Span {
                 range,
                 anchor: span::SpanAnchor {
-                    file_id: span::FileId::from_raw(0xe4e4e),
+                    file_id: span::EditionedFileId::new(
+                        span::FileId::from_raw(0xe4e4e),
+                        span::Edition::CURRENT,
+                    ),
                     ast_id: span::ROOT_ERASED_FILE_AST_ID,
                 },
                 ctx: SyntaxContextId::ROOT,
@@ -145,7 +153,7 @@ where
         } => TokenBuffer::from_tokens(token_trees),
         _ => TokenBuffer::from_subtree(tt),
     };
-    let parser_input = to_parser_input(&buffer);
+    let parser_input = to_parser_input(edition, &buffer);
     let parser_output = entry_point.parse(&parser_input, edition);
     let mut tree_sink = TtTreeSink::new(buffer.begin());
     for event in parser_output.iter() {
@@ -167,6 +175,7 @@ where
 /// Convert a string to a `TokenTree`. The spans of the subtree will be anchored to the provided
 /// anchor with the given context.
 pub fn parse_to_token_tree(
+    edition: Edition,
     anchor: SpanAnchor,
     ctx: Ctx,
     text: &str,
@@ -175,7 +184,7 @@ where
     SpanData: Copy + fmt::Debug,
     Ctx: Copy,
 {
-    let lexed = parser::LexedStr::new(text);
+    let lexed = parser::LexedStr::new(edition, text);
     if lexed.errors().next().is_some() {
         return None;
     }
@@ -185,11 +194,15 @@ where
 }
 
 /// Convert a string to a `TokenTree`. The passed span will be used for all spans of the produced subtree.
-pub fn parse_to_token_tree_static_span(span: S, text: &str) -> Option>
+pub fn parse_to_token_tree_static_span(
+    edition: Edition,
+    span: S,
+    text: &str,
+) -> Option>
 where
     S: Copy + fmt::Debug,
 {
-    let lexed = parser::LexedStr::new(text);
+    let lexed = parser::LexedStr::new(edition, text);
     if lexed.errors().next().is_some() {
         return None;
     }
@@ -199,15 +212,12 @@ where
 }
 
 /// Split token tree with separate expr: $($e:expr)SEP*
-pub fn parse_exprs_with_sep(
-    tt: &tt::Subtree,
+pub fn parse_exprs_with_sep(
+    tt: &tt::Subtree,
     sep: char,
-    span: S,
+    span: span::Span,
     edition: Edition,
-) -> Vec>
-where
-    S: Copy + fmt::Debug,
-{
+) -> Vec> {
     if tt.token_trees.is_empty() {
         return Vec::new();
     }
@@ -216,7 +226,12 @@ where
     let mut res = Vec::new();
 
     while iter.peek_n(0).is_some() {
-        let expanded = crate::expect_fragment(&mut iter, parser::PrefixEntryPoint::Expr, edition);
+        let expanded = crate::expect_fragment(
+            &mut iter,
+            parser::PrefixEntryPoint::Expr,
+            edition,
+            tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close },
+        );
 
         res.push(match expanded.value {
             None => break,
@@ -317,18 +332,29 @@ where
                         .into()
                 }
                 kind => {
-                    macro_rules! make_leaf {
-                        ($i:ident) => {
-                            tt::$i { span: conv.span_for(abs_range), text: token.to_text(conv) }
-                                .into()
+                    macro_rules! make_ident {
+                        () => {
+                            tt::Ident {
+                                span: conv.span_for(abs_range),
+                                sym: Symbol::intern(&token.to_text(conv)),
+                                is_raw: tt::IdentIsRaw::No,
+                            }
+                            .into()
                         };
                     }
                     let leaf: tt::Leaf<_> = match kind {
-                        T![true] | T![false] => make_leaf!(Ident),
-                        IDENT => make_leaf!(Ident),
-                        UNDERSCORE => make_leaf!(Ident),
-                        k if k.is_keyword() => make_leaf!(Ident),
-                        k if k.is_literal() => make_leaf!(Literal),
+                        T![true] | T![false] => make_ident!(),
+                        IDENT => {
+                            let text = token.to_text(conv);
+                            tt::Ident::new(&text, conv.span_for(abs_range)).into()
+                        }
+                        UNDERSCORE => make_ident!(),
+                        k if k.is_keyword() => make_ident!(),
+                        k if k.is_literal() => {
+                            let text = token.to_text(conv);
+                            let span = conv.span_for(abs_range);
+                            token_to_literal(&text, span).into()
+                        }
                         LIFETIME_IDENT => {
                             let apostrophe = tt::Leaf::from(tt::Punct {
                                 char: '\'',
@@ -339,11 +365,12 @@ where
                             token_trees.push(apostrophe.into());
 
                             let ident = tt::Leaf::from(tt::Ident {
-                                text: SmolStr::new(&token.to_text(conv)[1..]),
+                                sym: Symbol::intern(&token.to_text(conv)[1..]),
                                 span: conv.span_for(TextRange::new(
                                     abs_range.start() + TextSize::of('\''),
                                     abs_range.end(),
                                 )),
+                                is_raw: tt::IdentIsRaw::No,
                             });
                             token_trees.push(ident.into());
                             continue;
@@ -421,16 +448,10 @@ fn is_single_token_op(kind: SyntaxKind) -> bool {
 /// That is, strips leading `///` (or `/**`, etc)
 /// and strips the ending `*/`
 /// And then quote the string, which is needed to convert to `tt::Literal`
-fn doc_comment_text(comment: &ast::Comment, mode: DocCommentDesugarMode) -> SmolStr {
-    let prefix_len = comment.prefix().len();
-    let mut text = &comment.text()[prefix_len..];
-
-    // Remove ending "*/"
-    if comment.kind().shape == ast::CommentShape::Block {
-        text = &text[0..text.len() - 2];
-    }
-
-    let text = match mode {
+///
+/// Note that proc-macros desugar with string literals where as macro_rules macros desugar with raw string literals.
+pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (Symbol, tt::LitKind) {
+    match mode {
         DocCommentDesugarMode::Mbe => {
             let mut num_of_hashes = 0;
             let mut count = 0;
@@ -444,14 +465,13 @@ fn doc_comment_text(comment: &ast::Comment, mode: DocCommentDesugarMode) -> Smol
             }
 
             // Quote raw string with delimiters
-            // Note that `tt::Literal` expect an escaped string
-            format!(r#"r{delim}"{text}"{delim}"#, delim = "#".repeat(num_of_hashes))
+            (Symbol::intern(text), tt::LitKind::StrRaw(num_of_hashes))
         }
         // Quote string with delimiters
-        // Note that `tt::Literal` expect an escaped string
-        DocCommentDesugarMode::ProcMacro => format!(r#""{}""#, text.escape_debug()),
-    };
-    text.into()
+        DocCommentDesugarMode::ProcMacro => {
+            (Symbol::intern(&format_smolstr!("{}", text.escape_debug())), tt::LitKind::Str)
+        }
+    }
 }
 
 fn convert_doc_comment(
@@ -463,8 +483,13 @@ fn convert_doc_comment(
     let comment = ast::Comment::cast(token.clone())?;
     let doc = comment.kind().doc?;
 
-    let mk_ident =
-        |s: &str| tt::TokenTree::from(tt::Leaf::from(tt::Ident { text: s.into(), span }));
+    let mk_ident = |s: &str| {
+        tt::TokenTree::from(tt::Leaf::from(tt::Ident {
+            sym: Symbol::intern(s),
+            span,
+            is_raw: tt::IdentIsRaw::No,
+        }))
+    };
 
     let mk_punct = |c: char| {
         tt::TokenTree::from(tt::Leaf::from(tt::Punct {
@@ -475,7 +500,15 @@ fn convert_doc_comment(
     };
 
     let mk_doc_literal = |comment: &ast::Comment| {
-        let lit = tt::Literal { text: doc_comment_text(comment, mode), span };
+        let prefix_len = comment.prefix().len();
+        let mut text = &comment.text()[prefix_len..];
+
+        // Remove ending "*/"
+        if comment.kind().shape == ast::CommentShape::Block {
+            text = &text[0..text.len() - 2];
+        }
+        let (text, kind) = desugar_doc_comment_text(text, mode);
+        let lit = tt::Literal { symbol: text, span, kind, suffix: None };
 
         tt::TokenTree::from(tt::Leaf::from(lit))
     };
@@ -902,16 +935,22 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> {
 
 impl TtTreeSink<'_, Ctx>
 where
-    SpanData: Copy,
+    SpanData: Copy + fmt::Debug,
 {
     /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween.
     /// This occurs when a float literal is used as a field access.
     fn float_split(&mut self, has_pseudo_dot: bool) {
         let (text, span) = match self.cursor.token_tree() {
-            Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Literal(lit), _)) => {
-                (lit.text.as_str(), lit.span)
-            }
-            _ => unreachable!(),
+            Some(tt::buffer::TokenTreeRef::Leaf(
+                tt::Leaf::Literal(tt::Literal {
+                    symbol: text,
+                    span,
+                    kind: tt::LitKind::Float,
+                    suffix: _,
+                }),
+                _,
+            )) => (text.as_str(), *span),
+            tt => unreachable!("{tt:?}"),
         };
         // FIXME: Span splitting
         match text.split_once('.') {
@@ -954,7 +993,7 @@ where
         }
 
         let mut last = self.cursor;
-        for _ in 0..n_tokens {
+        'tokens: for _ in 0..n_tokens {
             let tmp: u8;
             if self.cursor.eof() {
                 break;
@@ -962,23 +1001,36 @@ where
             last = self.cursor;
             let (text, span) = loop {
                 break match self.cursor.token_tree() {
-                    Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
-                        // Mark the range if needed
-                        let (text, span) = match leaf {
-                            tt::Leaf::Ident(ident) => (ident.text.as_str(), ident.span),
-                            tt::Leaf::Punct(punct) => {
-                                assert!(punct.char.is_ascii());
-                                tmp = punct.char as u8;
-                                (
-                                    std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(),
-                                    punct.span,
-                                )
+                    Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => match leaf {
+                        tt::Leaf::Ident(ident) => {
+                            if ident.is_raw.yes() {
+                                self.buf.push_str("r#");
+                                self.text_pos += TextSize::of("r#");
                             }
-                            tt::Leaf::Literal(lit) => (lit.text.as_str(), lit.span),
-                        };
-                        self.cursor = self.cursor.bump();
-                        (text, span)
-                    }
+                            let r = (ident.sym.as_str(), ident.span);
+                            self.cursor = self.cursor.bump();
+                            r
+                        }
+                        tt::Leaf::Punct(punct) => {
+                            assert!(punct.char.is_ascii());
+                            tmp = punct.char as u8;
+                            let r = (
+                                std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(),
+                                punct.span,
+                            );
+                            self.cursor = self.cursor.bump();
+                            r
+                        }
+                        tt::Leaf::Literal(lit) => {
+                            let buf_l = self.buf.len();
+                            format_to!(self.buf, "{lit}");
+                            debug_assert_ne!(self.buf.len() - buf_l, 0);
+                            self.text_pos += TextSize::new((self.buf.len() - buf_l) as u32);
+                            self.token_map.push(self.text_pos, lit.span);
+                            self.cursor = self.cursor.bump();
+                            continue 'tokens;
+                        }
+                    },
                     Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
                         self.cursor = self.cursor.subtree().unwrap();
                         match delim_to_str(subtree.delimiter.kind, false) {
diff --git a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs
index 3f70149aa5eae..c35b28527a09b 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/to_parser_input.rs
@@ -3,11 +3,15 @@
 
 use std::fmt;
 
+use span::Edition;
 use syntax::{SyntaxKind, SyntaxKind::*, T};
 
 use tt::buffer::TokenBuffer;
 
-pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input {
+pub(crate) fn to_parser_input(
+    edition: Edition,
+    buffer: &TokenBuffer<'_, S>,
+) -> parser::Input {
     let mut res = parser::Input::default();
 
     let mut current = buffer.begin();
@@ -35,35 +39,39 @@ pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>)
             Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
                 match leaf {
                     tt::Leaf::Literal(lit) => {
-                        let is_negated = lit.text.starts_with('-');
-                        let inner_text = &lit.text[if is_negated { 1 } else { 0 }..];
-
-                        let kind = parser::LexedStr::single_token(inner_text)
-                            .map(|(kind, _error)| kind)
-                            .filter(|kind| {
-                                kind.is_literal()
-                                    && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
-                            })
-                            .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &lit));
-
+                        let kind = match lit.kind {
+                            tt::LitKind::Byte => SyntaxKind::BYTE,
+                            tt::LitKind::Char => SyntaxKind::CHAR,
+                            tt::LitKind::Integer => SyntaxKind::INT_NUMBER,
+                            tt::LitKind::Float => SyntaxKind::FLOAT_NUMBER,
+                            tt::LitKind::Str | tt::LitKind::StrRaw(_) => SyntaxKind::STRING,
+                            tt::LitKind::ByteStr | tt::LitKind::ByteStrRaw(_) => {
+                                SyntaxKind::BYTE_STRING
+                            }
+                            tt::LitKind::CStr | tt::LitKind::CStrRaw(_) => SyntaxKind::C_STRING,
+                            tt::LitKind::Err(_) => SyntaxKind::ERROR,
+                        };
                         res.push(kind);
 
-                        if kind == FLOAT_NUMBER && !inner_text.ends_with('.') {
+                        if kind == FLOAT_NUMBER && !lit.symbol.as_str().ends_with('.') {
                             // Tag the token as joint if it is float with a fractional part
                             // we use this jointness to inform the parser about what token split
                             // event to emit when we encounter a float literal in a field access
                             res.was_joint();
                         }
                     }
-                    tt::Leaf::Ident(ident) => match ident.text.as_ref() {
+                    tt::Leaf::Ident(ident) => match ident.sym.as_str() {
                         "_" => res.push(T![_]),
                         i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
-                        _ => match SyntaxKind::from_keyword(&ident.text) {
+                        _ if ident.is_raw.yes() => res.push(IDENT),
+                        "gen" if !edition.at_least_2024() => res.push(IDENT),
+                        "dyn" if !edition.at_least_2018() => res.push_ident(DYN_KW),
+                        "async" | "await" | "try" if !edition.at_least_2018() => res.push(IDENT),
+                        text => match SyntaxKind::from_keyword(text) {
                             Some(kind) => res.push(kind),
                             None => {
-                                let contextual_keyword =
-                                    SyntaxKind::from_contextual_keyword(&ident.text)
-                                        .unwrap_or(SyntaxKind::IDENT);
+                                let contextual_keyword = SyntaxKind::from_contextual_keyword(text)
+                                    .unwrap_or(SyntaxKind::IDENT);
                                 res.push_ident(contextual_keyword);
                             }
                         },
diff --git a/src/tools/rust-analyzer/crates/parser/src/edition.rs b/src/tools/rust-analyzer/crates/parser/src/edition.rs
index 26178544f9b5e..be0a2c794e58c 100644
--- a/src/tools/rust-analyzer/crates/parser/src/edition.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/edition.rs
@@ -4,6 +4,7 @@
 use std::fmt;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(u8)]
 pub enum Edition {
     Edition2015,
     Edition2018,
@@ -12,8 +13,23 @@ pub enum Edition {
 }
 
 impl Edition {
-    pub const CURRENT: Edition = Edition::Edition2021;
     pub const DEFAULT: Edition = Edition::Edition2015;
+    pub const LATEST: Edition = Edition::Edition2024;
+    pub const CURRENT: Edition = Edition::Edition2021;
+    /// The current latest stable edition, note this is usually not the right choice in code.
+    pub const CURRENT_FIXME: Edition = Edition::Edition2021;
+
+    pub fn at_least_2024(self) -> bool {
+        self >= Edition::Edition2024
+    }
+
+    pub fn at_least_2021(self) -> bool {
+        self >= Edition::Edition2021
+    }
+
+    pub fn at_least_2018(self) -> bool {
+        self >= Edition::Edition2018
+    }
 }
 
 #[derive(Debug)]
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 2930190cb330d..7ae1e5f82e563 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -165,42 +165,6 @@ pub(crate) mod entry {
             }
             m.complete(p, ERROR);
         }
-
-        pub(crate) fn eager_macro_input(p: &mut Parser<'_>) {
-            let m = p.start();
-
-            let closing_paren_kind = match p.current() {
-                T!['{'] => T!['}'],
-                T!['('] => T![')'],
-                T!['['] => T![']'],
-                _ => {
-                    p.error("expected `{`, `[`, `(`");
-                    while !p.at(EOF) {
-                        p.bump_any();
-                    }
-                    m.complete(p, ERROR);
-                    return;
-                }
-            };
-            p.bump_any();
-            while !p.at(EOF) && !p.at(closing_paren_kind) {
-                if expressions::expr(p).is_none() {
-                    break;
-                }
-                if !p.at(EOF) && !p.at(closing_paren_kind) {
-                    p.expect(T![,]);
-                }
-            }
-            p.expect(closing_paren_kind);
-            if p.at(EOF) {
-                m.complete(p, MACRO_EAGER_INPUT);
-                return;
-            }
-            while !p.at(EOF) {
-                p.bump_any();
-            }
-            m.complete(p, ERROR);
-        }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index 54ed5f0ba2368..a678c1f3a70e6 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -51,6 +51,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet =
         T![const],
         T![continue],
         T![do],
+        T![gen],
         T![for],
         T![if],
         T![let],
@@ -100,6 +101,8 @@ pub(super) fn atom_expr(
         }
         T![loop] => loop_expr(p, None),
         T![while] => while_expr(p, None),
+        // test try_macro_fallback 2015
+        // fn foo() { try!(Ok(())); }
         T![try] => try_block_expr(p, None),
         T![match] => match_expr(p),
         T![return] => return_expr(p),
@@ -138,15 +141,37 @@ pub(super) fn atom_expr(
         // fn f() { const { } }
         // fn f() { async { } }
         // fn f() { async move { } }
-        T![const] | T![unsafe] | T![async] if la == T!['{'] => {
+        T![const] | T![unsafe] | T![async] | T![gen] if la == T!['{'] => {
+            let m = p.start();
+            p.bump_any();
+            stmt_list(p);
+            m.complete(p, BLOCK_EXPR)
+        }
+        // test gen_blocks 2024
+        // pub fn main() {
+        //     gen { yield ""; };
+        //     async gen { yield ""; };
+        //     gen move { yield ""; };
+        //     async gen move { yield ""; };
+        // }
+        T![async] if la == T![gen] && p.nth(2) == T!['{'] => {
+            let m = p.start();
+            p.bump(T![async]);
+            p.eat(T![gen]);
+            stmt_list(p);
+            m.complete(p, BLOCK_EXPR)
+        }
+        T![async] | T![gen] if la == T![move] && p.nth(2) == T!['{'] => {
             let m = p.start();
             p.bump_any();
+            p.bump(T![move]);
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
         }
-        T![async] if la == T![move] && p.nth(2) == T!['{'] => {
+        T![async] if la == T![gen] && p.nth(2) == T![move] && p.nth(3) == T!['{'] => {
             let m = p.start();
             p.bump(T![async]);
+            p.bump(T![gen]);
             p.bump(T![move]);
             stmt_list(p);
             m.complete(p, BLOCK_EXPR)
@@ -355,6 +380,7 @@ fn closure_expr(p: &mut Parser<'_>) -> CompletedMarker {
     p.eat(T![const]);
     p.eat(T![static]);
     p.eat(T![async]);
+    p.eat(T![gen]);
     p.eat(T![move]);
 
     if !p.at(T![|]) {
@@ -743,24 +769,6 @@ fn break_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker {
 fn try_block_expr(p: &mut Parser<'_>, m: Option) -> CompletedMarker {
     assert!(p.at(T![try]));
     let m = m.unwrap_or_else(|| p.start());
-    // Special-case `try!` as macro.
-    // This is a hack until we do proper edition support
-    if p.nth_at(1, T![!]) {
-        // test try_macro_fallback
-        // fn foo() { try!(Ok(())); }
-        let macro_call = p.start();
-        let path = p.start();
-        let path_segment = p.start();
-        let name_ref = p.start();
-        p.bump_remap(IDENT);
-        name_ref.complete(p, NAME_REF);
-        path_segment.complete(p, PATH_SEGMENT);
-        path.complete(p, PATH);
-        let _block_like = items::macro_call_after_excl(p);
-        macro_call.complete(p, MACRO_CALL);
-        return m.complete(p, MACRO_EXPR);
-    }
-
     p.bump(T![try]);
     if p.at(T!['{']) {
         stmt_list(p);
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
index 6c05abc023878..cf80a535ac55f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
@@ -144,6 +144,12 @@ fn type_bound(p: &mut Parser<'_>) -> bool {
     match p.current() {
         LIFETIME_IDENT => lifetime(p),
         T![for] => types::for_type(p, false),
+        // test precise_capturing
+        // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {}
+        T![use] => {
+            p.bump_any();
+            generic_param_list(p)
+        }
         T![?] if p.nth_at(1, T![for]) => {
             // test question_for_type_trait_bound
             // fn f() where T: ?for<> Sized {}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index 99bbf47654be1..4e2a50d7a1fe7 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -112,11 +112,22 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
 
     // test_err async_without_semicolon
     // fn foo() { let _ = async {} }
-    if p.at(T![async]) && !matches!(p.nth(1), T!['{'] | T![move] | T![|]) {
+    if p.at(T![async])
+        && (!matches!(p.nth(1), T!['{'] | T![gen] | T![move] | T![|])
+            || matches!((p.nth(1), p.nth(2)), (T![gen], T![fn])))
+    {
         p.eat(T![async]);
         has_mods = true;
     }
 
+    // test_err gen_fn
+    // gen fn gen_fn() {}
+    // async gen fn async_gen_fn() {}
+    if p.at(T![gen]) && p.nth(1) == T![fn] {
+        p.eat(T![gen]);
+        has_mods = true;
+    }
+
     // test_err unsafe_block_in_mod
     // fn foo(){} unsafe { } fn bar(){}
     if p.at(T![unsafe]) && p.nth(1) != T!['{'] {
@@ -173,13 +184,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
         }
     }
 
-    // test existential_type
-    // existential type Foo: Fn() -> usize;
-    if p.at_contextual_kw(T![existential]) && p.nth(1) == T![type] {
-        p.bump_remap(T![existential]);
-        has_mods = true;
-    }
-
     // items
     match p.current() {
         T![fn] => fn_(p, m),
@@ -201,7 +205,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
 
         _ if has_visibility || has_mods => {
             if has_mods {
-                p.error("expected existential, fn, trait or impl");
+                p.error("expected fn, trait or impl");
             } else {
                 p.error("expected an item");
             }
@@ -226,13 +230,8 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
         IDENT if p.at_contextual_kw(T![union]) && p.nth(1) == IDENT => adt::union(p, m),
 
         T![macro] => macro_def(p, m),
-        // check if current token is "macro_rules" followed by "!" followed by an identifier or "try"
-        // try is keyword since the 2018 edition and the parser is not edition aware (yet!)
-        IDENT
-            if p.at_contextual_kw(T![macro_rules])
-                && p.nth_at(1, BANG)
-                && (p.nth_at(2, IDENT) || p.nth_at(2, T![try])) =>
-        {
+        // check if current token is "macro_rules" followed by "!" followed by an identifier
+        IDENT if p.at_contextual_kw(T![macro_rules]) && p.nth_at(1, BANG) && p.nth_at(2, IDENT) => {
             macro_rules(p, m)
         }
 
@@ -330,23 +329,14 @@ pub(crate) fn extern_item_list(p: &mut Parser<'_>) {
     m.complete(p, EXTERN_ITEM_LIST);
 }
 
+// test try_macro_rules 2015
+// macro_rules! try { () => {} }
 fn macro_rules(p: &mut Parser<'_>, m: Marker) {
     assert!(p.at_contextual_kw(T![macro_rules]));
     p.bump_remap(T![macro_rules]);
     p.expect(T![!]);
 
-    // Special-case `macro_rules! try`.
-    // This is a hack until we do proper edition support
-
-    // test try_macro_rules
-    // macro_rules! try { () => {} }
-    if p.at(T![try]) {
-        let m = p.start();
-        p.bump_remap(IDENT);
-        m.complete(p, NAME);
-    } else {
-        name(p);
-    }
+    name(p);
 
     match p.current() {
         // test macro_rules_non_brace
@@ -384,7 +374,7 @@ fn macro_def(p: &mut Parser<'_>, m: Marker) {
     m.complete(p, MACRO_DEF);
 }
 
-// test fn
+// test fn_
 // fn foo() {}
 fn fn_(p: &mut Parser<'_>, m: Marker) {
     p.bump(T![fn]);
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
index 18ec570cd5697..f4e57d3d6f3d0 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
@@ -49,6 +49,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
         T![dyn] => dyn_trait_type(p),
         // Some path types are not allowed to have bounds (no plus)
         T![<] => path_type_bounds(p, allow_bounds),
+        T![ident] if !p.edition().at_least_2018() && is_dyn_weak(p) => dyn_trait_type_weak(p),
         _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
         LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
         _ => {
@@ -57,6 +58,25 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
     }
 }
 
+fn is_dyn_weak(p: &Parser<'_>) -> bool {
+    const WEAK_DYN_PATH_FIRST: TokenSet = TokenSet::new(&[
+        IDENT,
+        T![self],
+        T![super],
+        T![crate],
+        T![Self],
+        T![lifetime_ident],
+        T![?],
+        T![for],
+        T!['('],
+    ]);
+
+    p.at_contextual_kw(T![dyn]) && {
+        let la = p.nth(1);
+        WEAK_DYN_PATH_FIRST.contains(la) && (la != T![:] || la != T![<])
+    }
+}
+
 pub(super) fn ascription(p: &mut Parser<'_>) {
     assert!(p.at(T![:]));
     p.bump(T![:]);
@@ -169,7 +189,7 @@ fn array_or_slice_type(p: &mut Parser<'_>) {
     m.complete(p, kind);
 }
 
-// test reference_type;
+// test reference_type
 // type A = &();
 // type B = &'static ();
 // type C = &mut ();
@@ -279,6 +299,23 @@ fn dyn_trait_type(p: &mut Parser<'_>) {
     m.complete(p, DYN_TRAIT_TYPE);
 }
 
+// test dyn_trait_type_weak 2015
+// type DynPlain = dyn Path;
+// type DynRef = &dyn Path;
+// type DynLt = dyn 'a + Path;
+// type DynQuestion = dyn ?Path;
+// type DynFor = dyn for<'a> Path;
+// type DynParen = dyn(Path);
+// type Path = dyn::Path;
+// type Generic = dyn;
+fn dyn_trait_type_weak(p: &mut Parser<'_>) {
+    assert!(p.at_contextual_kw(T![dyn]));
+    let m = p.start();
+    p.bump_remap(T![dyn]);
+    generic_params::bounds_without_colon(p);
+    m.complete(p, DYN_TRAIT_TYPE);
+}
+
 // test bare_dyn_types_with_leading_lifetime
 // type A = 'static + Trait;
 // type B = S<'static + Trait>;
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 52b24b7372578..13fc61074d091 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -13,6 +13,7 @@ use std::ops;
 use rustc_lexer::unescape::{EscapeError, Mode};
 
 use crate::{
+    Edition,
     SyntaxKind::{self, *},
     T,
 };
@@ -30,9 +31,9 @@ struct LexError {
 }
 
 impl<'a> LexedStr<'a> {
-    pub fn new(text: &'a str) -> LexedStr<'a> {
+    pub fn new(edition: Edition, text: &'a str) -> LexedStr<'a> {
         let _p = tracing::info_span!("LexedStr::new").entered();
-        let mut conv = Converter::new(text);
+        let mut conv = Converter::new(edition, text);
         if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
             conv.res.push(SHEBANG, conv.offset);
             conv.offset = shebang_len;
@@ -47,7 +48,7 @@ impl<'a> LexedStr<'a> {
         conv.finalize_with_eof()
     }
 
-    pub fn single_token(text: &'a str) -> Option<(SyntaxKind, Option)> {
+    pub fn single_token(edition: Edition, text: &'a str) -> Option<(SyntaxKind, Option)> {
         if text.is_empty() {
             return None;
         }
@@ -57,7 +58,7 @@ impl<'a> LexedStr<'a> {
             return None;
         }
 
-        let mut conv = Converter::new(text);
+        let mut conv = Converter::new(edition, text);
         conv.extend_token(&token.kind, text);
         match &*conv.res.kind {
             [kind] => Some((*kind, conv.res.error.pop().map(|it| it.msg))),
@@ -129,13 +130,15 @@ impl<'a> LexedStr<'a> {
 struct Converter<'a> {
     res: LexedStr<'a>,
     offset: usize,
+    edition: Edition,
 }
 
 impl<'a> Converter<'a> {
-    fn new(text: &'a str) -> Self {
+    fn new(edition: Edition, text: &'a str) -> Self {
         Self {
             res: LexedStr { text, kind: Vec::new(), start: Vec::new(), error: Vec::new() },
             offset: 0,
+            edition,
         }
     }
 
@@ -175,6 +178,17 @@ impl<'a> Converter<'a> {
                 rustc_lexer::TokenKind::Whitespace => WHITESPACE,
 
                 rustc_lexer::TokenKind::Ident if token_text == "_" => UNDERSCORE,
+                rustc_lexer::TokenKind::Ident
+                    if ["async", "await", "dyn", "try"].contains(&token_text)
+                        && !self.edition.at_least_2018() =>
+                {
+                    IDENT
+                }
+                rustc_lexer::TokenKind::Ident
+                    if token_text == "gen" && !self.edition.at_least_2024() =>
+                {
+                    IDENT
+                }
                 rustc_lexer::TokenKind::Ident => {
                     SyntaxKind::from_keyword(token_text).unwrap_or(IDENT)
                 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs
index 738ed239a7c9d..679492066a383 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs
@@ -82,8 +82,6 @@ pub enum TopEntryPoint {
     /// Edge case -- macros generally don't expand to attributes, with the
     /// exception of `cfg_attr` which does!
     MetaItem,
-    /// Edge case 2 -- eager macros expand their input to a delimited list of comma separated expressions
-    MacroEagerInput,
 }
 
 impl TopEntryPoint {
@@ -97,7 +95,6 @@ impl TopEntryPoint {
             TopEntryPoint::Type => grammar::entry::top::type_,
             TopEntryPoint::Expr => grammar::entry::top::expr,
             TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
-            TopEntryPoint::MacroEagerInput => grammar::entry::top::eager_macro_input,
         };
         let mut p = parser::Parser::new(input, edition);
         entry_point(&mut p);
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 5b901f911dcc4..7d3eb5de25f9a 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -27,14 +27,14 @@ pub(crate) struct Parser<'t> {
     pos: usize,
     events: Vec,
     steps: Cell,
-    _edition: Edition,
+    edition: Edition,
 }
 
 static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000);
 
 impl<'t> Parser<'t> {
     pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
-        Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), _edition: edition }
+        Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), edition }
     }
 
     pub(crate) fn finish(self) -> Vec {
@@ -277,6 +277,10 @@ impl<'t> Parser<'t> {
     fn push_event(&mut self, event: Event) {
         self.events.push(event);
     }
+
+    pub(crate) fn edition(&self) -> Edition {
+        self.edition
+    }
 }
 
 /// See [`Parser::start`].
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 7f49cc087aecf..1cf81e79b03cf 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -12,7 +12,7 @@
 use std::mem;
 
 use crate::{
-    LexedStr, Step,
+    Edition, LexedStr, Step,
     SyntaxKind::{self, *},
 };
 
@@ -25,7 +25,7 @@ pub enum StrStep<'a> {
 }
 
 impl LexedStr<'_> {
-    pub fn to_input(&self) -> crate::Input {
+    pub fn to_input(&self, edition: Edition) -> crate::Input {
         let _p = tracing::info_span!("LexedStr::to_input").entered();
         let mut res = crate::Input::default();
         let mut was_joint = false;
@@ -35,8 +35,11 @@ impl LexedStr<'_> {
                 was_joint = false
             } else if kind == SyntaxKind::IDENT {
                 let token_text = self.text(i);
-                let contextual_kw =
-                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT);
+                let contextual_kw = if !edition.at_least_2018() && token_text == "dyn" {
+                    SyntaxKind::DYN_KW
+                } else {
+                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT)
+                };
                 res.push_ident(contextual_kw);
             } else {
                 if was_joint {
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index ad3398453bee0..7bddf88740136 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -9,6 +9,7 @@ pub enum SyntaxKind {
     TOMBSTONE,
     #[doc(hidden)]
     EOF,
+    DOLLAR,
     SEMICOLON,
     COMMA,
     L_PAREN,
@@ -23,7 +24,6 @@ pub enum SyntaxKind {
     POUND,
     TILDE,
     QUESTION,
-    DOLLAR,
     AMP,
     PIPE,
     PLUS,
@@ -61,6 +61,7 @@ pub enum SyntaxKind {
     SHR,
     SHLEQ,
     SHREQ,
+    SELF_TYPE_KW,
     ABSTRACT_KW,
     AS_KW,
     ASYNC_KW,
@@ -80,6 +81,7 @@ pub enum SyntaxKind {
     FINAL_KW,
     FN_KW,
     FOR_KW,
+    GEN_KW,
     IF_KW,
     IMPL_KW,
     IN_KW,
@@ -96,7 +98,6 @@ pub enum SyntaxKind {
     REF_KW,
     RETURN_KW,
     SELF_KW,
-    SELF_TYPE_KW,
     STATIC_KW,
     STRUCT_KW,
     SUPER_KW,
@@ -112,173 +113,184 @@ pub enum SyntaxKind {
     WHERE_KW,
     WHILE_KW,
     YIELD_KW,
+    ASM_KW,
     AUTO_KW,
     BUILTIN_KW,
     DEFAULT_KW,
-    EXISTENTIAL_KW,
-    UNION_KW,
-    RAW_KW,
+    FORMAT_ARGS_KW,
     MACRO_RULES_KW,
-    YEET_KW,
     OFFSET_OF_KW,
-    ASM_KW,
-    FORMAT_ARGS_KW,
-    INT_NUMBER,
-    FLOAT_NUMBER,
-    CHAR,
+    RAW_KW,
+    UNION_KW,
+    YEET_KW,
     BYTE,
-    STRING,
     BYTE_STRING,
+    CHAR,
     C_STRING,
+    FLOAT_NUMBER,
+    INT_NUMBER,
+    RAW_BYTE_STRING,
+    RAW_C_STRING,
+    RAW_STRING,
+    STRING,
+    COMMENT,
     ERROR,
     IDENT,
-    WHITESPACE,
     LIFETIME_IDENT,
-    COMMENT,
+    NEWLINE,
     SHEBANG,
-    SOURCE_FILE,
-    STRUCT,
-    UNION,
+    WHITESPACE,
+    ABI,
+    ADT,
+    ARG_LIST,
+    ARRAY_EXPR,
+    ARRAY_TYPE,
+    ASM_EXPR,
+    ASSOC_ITEM,
+    ASSOC_ITEM_LIST,
+    ASSOC_TYPE_ARG,
+    ATTR,
+    AWAIT_EXPR,
+    BECOME_EXPR,
+    BIN_EXPR,
+    BLOCK_EXPR,
+    BOX_PAT,
+    BREAK_EXPR,
+    CALL_EXPR,
+    CAST_EXPR,
+    CLOSURE_BINDER,
+    CLOSURE_EXPR,
+    CONST,
+    CONST_ARG,
+    CONST_BLOCK_PAT,
+    CONST_PARAM,
+    CONTINUE_EXPR,
+    DYN_TRAIT_TYPE,
     ENUM,
-    FN,
-    RET_TYPE,
+    EXPR,
+    EXPR_STMT,
+    EXTERN_BLOCK,
     EXTERN_CRATE,
-    MODULE,
-    USE,
-    STATIC,
-    CONST,
-    TRAIT,
-    TRAIT_ALIAS,
-    IMPL,
-    TYPE_ALIAS,
-    MACRO_CALL,
-    MACRO_RULES,
-    MACRO_ARM,
-    TOKEN_TREE,
-    MACRO_DEF,
-    PAREN_TYPE,
-    TUPLE_TYPE,
-    MACRO_TYPE,
-    NEVER_TYPE,
-    PATH_TYPE,
-    PTR_TYPE,
-    ARRAY_TYPE,
-    SLICE_TYPE,
-    REF_TYPE,
-    INFER_TYPE,
+    EXTERN_ITEM,
+    EXTERN_ITEM_LIST,
+    FIELD_EXPR,
+    FIELD_LIST,
+    FN,
     FN_PTR_TYPE,
+    FORMAT_ARGS_ARG,
+    FORMAT_ARGS_EXPR,
+    FOR_EXPR,
     FOR_TYPE,
-    IMPL_TRAIT_TYPE,
-    DYN_TRAIT_TYPE,
-    OR_PAT,
-    PAREN_PAT,
-    REF_PAT,
-    BOX_PAT,
+    GENERIC_ARG,
+    GENERIC_ARG_LIST,
+    GENERIC_PARAM,
+    GENERIC_PARAM_LIST,
     IDENT_PAT,
-    WILDCARD_PAT,
-    REST_PAT,
-    PATH_PAT,
-    RECORD_PAT,
-    RECORD_PAT_FIELD_LIST,
-    RECORD_PAT_FIELD,
-    TUPLE_STRUCT_PAT,
-    TUPLE_PAT,
-    SLICE_PAT,
-    RANGE_PAT,
-    LITERAL_PAT,
-    MACRO_PAT,
-    CONST_BLOCK_PAT,
-    TUPLE_EXPR,
-    ARRAY_EXPR,
-    PAREN_EXPR,
-    PATH_EXPR,
-    CLOSURE_EXPR,
     IF_EXPR,
-    WHILE_EXPR,
-    LOOP_EXPR,
-    FOR_EXPR,
-    CONTINUE_EXPR,
-    BREAK_EXPR,
+    IMPL,
+    IMPL_TRAIT_TYPE,
+    INDEX_EXPR,
+    INFER_TYPE,
+    ITEM,
+    ITEM_LIST,
     LABEL,
-    BLOCK_EXPR,
-    STMT_LIST,
-    RETURN_EXPR,
-    BECOME_EXPR,
-    YIELD_EXPR,
-    YEET_EXPR,
+    LET_ELSE,
     LET_EXPR,
-    UNDERSCORE_EXPR,
+    LET_STMT,
+    LIFETIME,
+    LIFETIME_ARG,
+    LIFETIME_PARAM,
+    LITERAL,
+    LITERAL_PAT,
+    LOOP_EXPR,
+    MACRO_CALL,
+    MACRO_DEF,
     MACRO_EXPR,
-    MATCH_EXPR,
-    MATCH_ARM_LIST,
+    MACRO_ITEMS,
+    MACRO_PAT,
+    MACRO_RULES,
+    MACRO_STMTS,
+    MACRO_TYPE,
     MATCH_ARM,
+    MATCH_ARM_LIST,
+    MATCH_EXPR,
     MATCH_GUARD,
-    RECORD_EXPR,
-    RECORD_EXPR_FIELD_LIST,
-    RECORD_EXPR_FIELD,
-    OFFSET_OF_EXPR,
-    ASM_EXPR,
-    FORMAT_ARGS_EXPR,
-    FORMAT_ARGS_ARG,
-    CALL_EXPR,
-    INDEX_EXPR,
+    META,
     METHOD_CALL_EXPR,
-    FIELD_EXPR,
-    AWAIT_EXPR,
-    TRY_EXPR,
-    CAST_EXPR,
-    REF_EXPR,
+    MODULE,
+    NAME,
+    NAME_REF,
+    NEVER_TYPE,
+    OFFSET_OF_EXPR,
+    OR_PAT,
+    PARAM,
+    PARAM_LIST,
+    PAREN_EXPR,
+    PAREN_PAT,
+    PAREN_TYPE,
+    PAT,
+    PATH,
+    PATH_EXPR,
+    PATH_PAT,
+    PATH_SEGMENT,
+    PATH_TYPE,
     PREFIX_EXPR,
+    PTR_TYPE,
     RANGE_EXPR,
-    BIN_EXPR,
-    EXTERN_BLOCK,
-    EXTERN_ITEM_LIST,
-    VARIANT,
-    RECORD_FIELD_LIST,
+    RANGE_PAT,
+    RECORD_EXPR,
+    RECORD_EXPR_FIELD,
+    RECORD_EXPR_FIELD_LIST,
     RECORD_FIELD,
-    TUPLE_FIELD_LIST,
+    RECORD_FIELD_LIST,
+    RECORD_PAT,
+    RECORD_PAT_FIELD,
+    RECORD_PAT_FIELD_LIST,
+    REF_EXPR,
+    REF_PAT,
+    REF_TYPE,
+    RENAME,
+    REST_PAT,
+    RETURN_EXPR,
+    RET_TYPE,
+    SELF_PARAM,
+    SLICE_PAT,
+    SLICE_TYPE,
+    SOURCE_FILE,
+    STATIC,
+    STMT,
+    STMT_LIST,
+    STRUCT,
+    TOKEN_TREE,
+    TRAIT,
+    TRAIT_ALIAS,
+    TRY_EXPR,
+    TUPLE_EXPR,
     TUPLE_FIELD,
-    VARIANT_LIST,
-    ITEM_LIST,
-    ASSOC_ITEM_LIST,
-    ATTR,
-    META,
+    TUPLE_FIELD_LIST,
+    TUPLE_PAT,
+    TUPLE_STRUCT_PAT,
+    TUPLE_TYPE,
+    TYPE,
+    TYPE_ALIAS,
+    TYPE_ARG,
+    TYPE_BOUND,
+    TYPE_BOUND_LIST,
+    TYPE_PARAM,
+    UNDERSCORE_EXPR,
+    UNION,
+    USE,
     USE_TREE,
     USE_TREE_LIST,
-    PATH,
-    PATH_SEGMENT,
-    LITERAL,
-    RENAME,
+    VARIANT,
+    VARIANT_LIST,
     VISIBILITY,
     WHERE_CLAUSE,
     WHERE_PRED,
-    ABI,
-    NAME,
-    NAME_REF,
-    LET_STMT,
-    LET_ELSE,
-    EXPR_STMT,
-    GENERIC_PARAM_LIST,
-    GENERIC_PARAM,
-    LIFETIME_PARAM,
-    TYPE_PARAM,
-    RETURN_TYPE_ARG,
-    CONST_PARAM,
-    GENERIC_ARG_LIST,
-    LIFETIME,
-    LIFETIME_ARG,
-    TYPE_ARG,
-    ASSOC_TYPE_ARG,
-    CONST_ARG,
-    PARAM_LIST,
-    PARAM,
-    SELF_PARAM,
-    ARG_LIST,
-    TYPE_BOUND,
-    TYPE_BOUND_LIST,
-    MACRO_ITEMS,
-    MACRO_STMTS,
-    MACRO_EAGER_INPUT,
+    WHILE_EXPR,
+    WILDCARD_PAT,
+    YEET_EXPR,
+    YIELD_EXPR,
     #[doc(hidden)]
     __LAST,
 }
@@ -287,7 +299,8 @@ impl SyntaxKind {
     pub fn is_keyword(self) -> bool {
         matches!(
             self,
-            ABSTRACT_KW
+            SELF_TYPE_KW
+                | ABSTRACT_KW
                 | AS_KW
                 | ASYNC_KW
                 | AWAIT_KW
@@ -306,6 +319,7 @@ impl SyntaxKind {
                 | FINAL_KW
                 | FN_KW
                 | FOR_KW
+                | GEN_KW
                 | IF_KW
                 | IMPL_KW
                 | IN_KW
@@ -322,7 +336,6 @@ impl SyntaxKind {
                 | REF_KW
                 | RETURN_KW
                 | SELF_KW
-                | SELF_TYPE_KW
                 | STATIC_KW
                 | STRUCT_KW
                 | SUPER_KW
@@ -338,23 +351,23 @@ impl SyntaxKind {
                 | WHERE_KW
                 | WHILE_KW
                 | YIELD_KW
+                | ASM_KW
                 | AUTO_KW
                 | BUILTIN_KW
                 | DEFAULT_KW
-                | EXISTENTIAL_KW
-                | UNION_KW
-                | RAW_KW
+                | FORMAT_ARGS_KW
                 | MACRO_RULES_KW
-                | YEET_KW
                 | OFFSET_OF_KW
-                | ASM_KW
-                | FORMAT_ARGS_KW
+                | RAW_KW
+                | UNION_KW
+                | YEET_KW
         )
     }
     pub fn is_punct(self) -> bool {
         matches!(
             self,
-            SEMICOLON
+            DOLLAR
+                | SEMICOLON
                 | COMMA
                 | L_PAREN
                 | R_PAREN
@@ -368,7 +381,6 @@ impl SyntaxKind {
                 | POUND
                 | TILDE
                 | QUESTION
-                | DOLLAR
                 | AMP
                 | PIPE
                 | PLUS
@@ -409,10 +421,22 @@ impl SyntaxKind {
         )
     }
     pub fn is_literal(self) -> bool {
-        matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING | C_STRING)
+        matches!(
+            self,
+            BYTE | BYTE_STRING
+                | CHAR
+                | C_STRING
+                | FLOAT_NUMBER
+                | INT_NUMBER
+                | RAW_BYTE_STRING
+                | RAW_C_STRING
+                | RAW_STRING
+                | STRING
+        )
     }
     pub fn from_keyword(ident: &str) -> Option {
         let kw = match ident {
+            "Self" => SELF_TYPE_KW,
             "abstract" => ABSTRACT_KW,
             "as" => AS_KW,
             "async" => ASYNC_KW,
@@ -432,6 +456,7 @@ impl SyntaxKind {
             "final" => FINAL_KW,
             "fn" => FN_KW,
             "for" => FOR_KW,
+            "gen" => GEN_KW,
             "if" => IF_KW,
             "impl" => IMPL_KW,
             "in" => IN_KW,
@@ -448,7 +473,6 @@ impl SyntaxKind {
             "ref" => REF_KW,
             "return" => RETURN_KW,
             "self" => SELF_KW,
-            "Self" => SELF_TYPE_KW,
             "static" => STATIC_KW,
             "struct" => STRUCT_KW,
             "super" => SUPER_KW,
@@ -470,23 +494,23 @@ impl SyntaxKind {
     }
     pub fn from_contextual_keyword(ident: &str) -> Option {
         let kw = match ident {
+            "asm" => ASM_KW,
             "auto" => AUTO_KW,
             "builtin" => BUILTIN_KW,
             "default" => DEFAULT_KW,
-            "existential" => EXISTENTIAL_KW,
-            "union" => UNION_KW,
-            "raw" => RAW_KW,
+            "format_args" => FORMAT_ARGS_KW,
             "macro_rules" => MACRO_RULES_KW,
-            "yeet" => YEET_KW,
             "offset_of" => OFFSET_OF_KW,
-            "asm" => ASM_KW,
-            "format_args" => FORMAT_ARGS_KW,
+            "raw" => RAW_KW,
+            "union" => UNION_KW,
+            "yeet" => YEET_KW,
             _ => return None,
         };
         Some(kw)
     }
     pub fn from_char(c: char) -> Option {
         let tok = match c {
+            '$' => DOLLAR,
             ';' => SEMICOLON,
             ',' => COMMA,
             '(' => L_PAREN,
@@ -501,7 +525,6 @@ impl SyntaxKind {
             '#' => POUND,
             '~' => TILDE,
             '?' => QUESTION,
-            '$' => DOLLAR,
             '&' => AMP,
             '|' => PIPE,
             '+' => PLUS,
@@ -521,4 +544,4 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests.rs b/src/tools/rust-analyzer/crates/parser/src/tests.rs
index a38689791c403..e7bccb6685c95 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests.rs
@@ -9,7 +9,11 @@ use std::{
 
 use expect_test::expect_file;
 
-use crate::{LexedStr, TopEntryPoint};
+use crate::{Edition, LexedStr, TopEntryPoint};
+
+#[rustfmt::skip]
+#[path = "../test_data/generated/runner.rs"]
+mod runner;
 
 #[test]
 fn lex_ok() {
@@ -30,7 +34,7 @@ fn lex_err() {
 }
 
 fn lex(text: &str) -> String {
-    let lexed = LexedStr::new(text);
+    let lexed = LexedStr::new(Edition::CURRENT, text);
 
     let mut res = String::new();
     for i in 0..lexed.len() {
@@ -48,17 +52,7 @@ fn lex(text: &str) -> String {
 fn parse_ok() {
     for case in TestCase::list("parser/ok") {
         let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
-        let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
-        assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
-        expect_file![case.rast].assert_eq(&actual);
-    }
-}
-
-#[test]
-fn parse_inline_ok() {
-    for case in TestCase::list("parser/inline/ok") {
-        let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
-        let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
+        let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text, Edition::CURRENT);
         assert!(!errors, "errors in an OK file {}:\n{actual}", case.rs.display());
         expect_file![case.rast].assert_eq(&actual);
     }
@@ -68,26 +62,16 @@ fn parse_inline_ok() {
 fn parse_err() {
     for case in TestCase::list("parser/err") {
         let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
-        let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
+        let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text, Edition::CURRENT);
         assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
         expect_file![case.rast].assert_eq(&actual)
     }
 }
 
-#[test]
-fn parse_inline_err() {
-    for case in TestCase::list("parser/inline/err") {
-        let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
-        let (actual, errors) = parse(TopEntryPoint::SourceFile, &case.text);
-        assert!(errors, "no errors in an ERR file {}:\n{actual}", case.rs.display());
-        expect_file![case.rast].assert_eq(&actual)
-    }
-}
-
-fn parse(entry: TopEntryPoint, text: &str) -> (String, bool) {
-    let lexed = LexedStr::new(text);
-    let input = lexed.to_input();
-    let output = entry.parse(&input, crate::Edition::CURRENT);
+fn parse(entry: TopEntryPoint, text: &str, edition: Edition) -> (String, bool) {
+    let lexed = LexedStr::new(edition, text);
+    let input = lexed.to_input(edition);
+    let output = entry.parse(&input, edition);
 
     let mut buf = String::new();
     let mut errors = Vec::new();
@@ -167,3 +151,37 @@ impl TestCase {
         res
     }
 }
+
+#[track_caller]
+fn run_and_expect_no_errors(path: &str) {
+    run_and_expect_no_errors_with_edition(path, Edition::CURRENT)
+}
+
+#[track_caller]
+fn run_and_expect_errors(path: &str) {
+    run_and_expect_errors_with_edition(path, Edition::CURRENT)
+}
+
+#[track_caller]
+fn run_and_expect_no_errors_with_edition(path: &str, edition: Edition) {
+    let path = PathBuf::from(path);
+    let text = std::fs::read_to_string(&path).unwrap();
+    let (actual, errors) = parse(TopEntryPoint::SourceFile, &text, edition);
+    assert!(!errors, "errors in an OK file {}:\n{actual}", path.display());
+    let mut p = PathBuf::from("..");
+    p.push(path);
+    p.set_extension("rast");
+    expect_file![p].assert_eq(&actual)
+}
+
+#[track_caller]
+fn run_and_expect_errors_with_edition(path: &str, edition: Edition) {
+    let path = PathBuf::from(path);
+    let text = std::fs::read_to_string(&path).unwrap();
+    let (actual, errors) = parse(TopEntryPoint::SourceFile, &text, edition);
+    assert!(errors, "no errors in an ERR file {}:\n{actual}", path.display());
+    let mut p = PathBuf::from("..");
+    p.push(path);
+    p.set_extension("rast");
+    expect_file![p].assert_eq(&actual)
+}
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
index f92b39edb7656..e2268eed60ab9 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
@@ -1,4 +1,4 @@
-use crate::{LexedStr, PrefixEntryPoint, Step};
+use crate::{Edition, LexedStr, PrefixEntryPoint, Step};
 
 #[test]
 fn vis() {
@@ -82,11 +82,11 @@ fn meta_item() {
 
 #[track_caller]
 fn check(entry: PrefixEntryPoint, input: &str, prefix: &str) {
-    let lexed = LexedStr::new(input);
-    let input = lexed.to_input();
+    let lexed = LexedStr::new(Edition::CURRENT, input);
+    let input = lexed.to_input(Edition::CURRENT);
 
     let mut n_tokens = 0;
-    for step in entry.parse(&input, crate::Edition::CURRENT).iter() {
+    for step in entry.parse(&input, Edition::CURRENT).iter() {
         match step {
             Step::Token { n_input_tokens, .. } => n_tokens += n_input_tokens as usize,
             Step::FloatSplit { .. } => n_tokens += 1,
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/top_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/top_entries.rs
index 49dd9e293b8fe..c56bf0b64485a 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/top_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/top_entries.rs
@@ -307,6 +307,6 @@ fn expr() {
 
 #[track_caller]
 fn check(entry: TopEntryPoint, input: &str, expect: expect_test::Expect) {
-    let (parsed, _errors) = super::parse(entry, input);
+    let (parsed, _errors) = super::parse(entry, input, crate::Edition::CURRENT);
     expect.assert_eq(&parsed)
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
new file mode 100644
index 0000000000000..1907f03b44d11
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -0,0 +1,832 @@
+mod ok {
+    use crate::tests::*;
+    #[test]
+    fn anonymous_const() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/anonymous_const.rs");
+    }
+    #[test]
+    fn arb_self_types() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/arb_self_types.rs");
+    }
+    #[test]
+    fn arg_with_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/arg_with_attr.rs"); }
+    #[test]
+    fn array_attrs() { run_and_expect_no_errors("test_data/parser/inline/ok/array_attrs.rs"); }
+    #[test]
+    fn array_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/array_expr.rs"); }
+    #[test]
+    fn array_type() { run_and_expect_no_errors("test_data/parser/inline/ok/array_type.rs"); }
+    #[test]
+    fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); }
+    #[test]
+    fn assoc_const_eq() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs");
+    }
+    #[test]
+    fn assoc_item_list() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/assoc_item_list.rs");
+    }
+    #[test]
+    fn assoc_item_list_inner_attrs() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/assoc_item_list_inner_attrs.rs");
+    }
+    #[test]
+    fn assoc_type_bound() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/assoc_type_bound.rs");
+    }
+    #[test]
+    fn assoc_type_eq() { run_and_expect_no_errors("test_data/parser/inline/ok/assoc_type_eq.rs"); }
+    #[test]
+    fn associated_return_type_bounds() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/associated_return_type_bounds.rs");
+    }
+    #[test]
+    fn associated_type_bounds() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/associated_type_bounds.rs");
+    }
+    #[test]
+    fn async_trait_bound() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/async_trait_bound.rs");
+    }
+    #[test]
+    fn attr_on_expr_stmt() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/attr_on_expr_stmt.rs");
+    }
+    #[test]
+    fn await_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/await_expr.rs"); }
+    #[test]
+    fn bare_dyn_types_with_leading_lifetime() {
+        run_and_expect_no_errors(
+            "test_data/parser/inline/ok/bare_dyn_types_with_leading_lifetime.rs",
+        );
+    }
+    #[test]
+    fn bare_dyn_types_with_paren_as_generic_args() {
+        run_and_expect_no_errors(
+            "test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs",
+        );
+    }
+    #[test]
+    fn become_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/become_expr.rs"); }
+    #[test]
+    fn bind_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/bind_pat.rs"); }
+    #[test]
+    fn binop_resets_statementness() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/binop_resets_statementness.rs");
+    }
+    #[test]
+    fn block() { run_and_expect_no_errors("test_data/parser/inline/ok/block.rs"); }
+    #[test]
+    fn block_items() { run_and_expect_no_errors("test_data/parser/inline/ok/block_items.rs"); }
+    #[test]
+    fn box_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/box_pat.rs"); }
+    #[test]
+    fn break_ambiguity() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/break_ambiguity.rs");
+    }
+    #[test]
+    fn break_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/break_expr.rs"); }
+    #[test]
+    fn builtin_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/builtin_expr.rs"); }
+    #[test]
+    fn call_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/call_expr.rs"); }
+    #[test]
+    fn cast_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/cast_expr.rs"); }
+    #[test]
+    fn closure_body_underscore_assignment() {
+        run_and_expect_no_errors(
+            "test_data/parser/inline/ok/closure_body_underscore_assignment.rs",
+        );
+    }
+    #[test]
+    fn closure_params() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/closure_params.rs");
+    }
+    #[test]
+    fn closure_range_method_call() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/closure_range_method_call.rs");
+    }
+    #[test]
+    fn const_arg() { run_and_expect_no_errors("test_data/parser/inline/ok/const_arg.rs"); }
+    #[test]
+    fn const_arg_block() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_arg_block.rs");
+    }
+    #[test]
+    fn const_arg_bool_literal() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_arg_bool_literal.rs");
+    }
+    #[test]
+    fn const_arg_literal() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_arg_literal.rs");
+    }
+    #[test]
+    fn const_arg_negative_number() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_arg_negative_number.rs");
+    }
+    #[test]
+    fn const_block_pat() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_block_pat.rs");
+    }
+    #[test]
+    fn const_closure() { run_and_expect_no_errors("test_data/parser/inline/ok/const_closure.rs"); }
+    #[test]
+    fn const_item() { run_and_expect_no_errors("test_data/parser/inline/ok/const_item.rs"); }
+    #[test]
+    fn const_param() { run_and_expect_no_errors("test_data/parser/inline/ok/const_param.rs"); }
+    #[test]
+    fn const_param_default_expression() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_param_default_expression.rs");
+    }
+    #[test]
+    fn const_param_default_literal() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_param_default_literal.rs");
+    }
+    #[test]
+    fn const_param_default_path() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_param_default_path.rs");
+    }
+    #[test]
+    fn const_trait_bound() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/const_trait_bound.rs");
+    }
+    #[test]
+    fn continue_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/continue_expr.rs"); }
+    #[test]
+    fn crate_path() { run_and_expect_no_errors("test_data/parser/inline/ok/crate_path.rs"); }
+    #[test]
+    fn crate_visibility() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/crate_visibility.rs");
+    }
+    #[test]
+    fn crate_visibility_in() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/crate_visibility_in.rs");
+    }
+    #[test]
+    fn default_async_fn() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/default_async_fn.rs");
+    }
+    #[test]
+    fn default_async_unsafe_fn() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/default_async_unsafe_fn.rs");
+    }
+    #[test]
+    fn default_item() { run_and_expect_no_errors("test_data/parser/inline/ok/default_item.rs"); }
+    #[test]
+    fn default_unsafe_item() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/default_unsafe_item.rs");
+    }
+    #[test]
+    fn destructuring_assignment_struct_rest_pattern() {
+        run_and_expect_no_errors(
+            "test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rs",
+        );
+    }
+    #[test]
+    fn destructuring_assignment_wildcard_pat() {
+        run_and_expect_no_errors(
+            "test_data/parser/inline/ok/destructuring_assignment_wildcard_pat.rs",
+        );
+    }
+    #[test]
+    fn dot_dot_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/dot_dot_pat.rs"); }
+    #[test]
+    fn dyn_trait_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/dyn_trait_type.rs");
+    }
+    #[test]
+    fn dyn_trait_type_weak() {
+        run_and_expect_no_errors_with_edition(
+            "test_data/parser/inline/ok/dyn_trait_type_weak.rs",
+            crate::Edition::Edition2015,
+        );
+    }
+    #[test]
+    fn effect_blocks() { run_and_expect_no_errors("test_data/parser/inline/ok/effect_blocks.rs"); }
+    #[test]
+    fn exclusive_range_pat() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/exclusive_range_pat.rs");
+    }
+    #[test]
+    fn expr_literals() { run_and_expect_no_errors("test_data/parser/inline/ok/expr_literals.rs"); }
+    #[test]
+    fn expression_after_block() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/expression_after_block.rs");
+    }
+    #[test]
+    fn extern_block() { run_and_expect_no_errors("test_data/parser/inline/ok/extern_block.rs"); }
+    #[test]
+    fn extern_crate() { run_and_expect_no_errors("test_data/parser/inline/ok/extern_crate.rs"); }
+    #[test]
+    fn extern_crate_rename() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/extern_crate_rename.rs");
+    }
+    #[test]
+    fn extern_crate_self() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/extern_crate_self.rs");
+    }
+    #[test]
+    fn field_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/field_expr.rs"); }
+    #[test]
+    fn fn_() { run_and_expect_no_errors("test_data/parser/inline/ok/fn_.rs"); }
+    #[test]
+    fn fn_decl() { run_and_expect_no_errors("test_data/parser/inline/ok/fn_decl.rs"); }
+    #[test]
+    fn fn_def_param() { run_and_expect_no_errors("test_data/parser/inline/ok/fn_def_param.rs"); }
+    #[test]
+    fn fn_pointer_param_ident_path() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/fn_pointer_param_ident_path.rs");
+    }
+    #[test]
+    fn fn_pointer_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/fn_pointer_type.rs");
+    }
+    #[test]
+    fn fn_pointer_type_with_ret() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/fn_pointer_type_with_ret.rs");
+    }
+    #[test]
+    fn fn_pointer_unnamed_arg() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/fn_pointer_unnamed_arg.rs");
+    }
+    #[test]
+    fn for_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/for_expr.rs"); }
+    #[test]
+    fn for_range_from() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/for_range_from.rs");
+    }
+    #[test]
+    fn for_type() { run_and_expect_no_errors("test_data/parser/inline/ok/for_type.rs"); }
+    #[test]
+    fn full_range_expr() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/full_range_expr.rs");
+    }
+    #[test]
+    fn function_ret_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/function_ret_type.rs");
+    }
+    #[test]
+    fn function_type_params() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/function_type_params.rs");
+    }
+    #[test]
+    fn function_where_clause() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/function_where_clause.rs");
+    }
+    #[test]
+    fn gen_blocks() {
+        run_and_expect_no_errors_with_edition(
+            "test_data/parser/inline/ok/gen_blocks.rs",
+            crate::Edition::Edition2024,
+        );
+    }
+    #[test]
+    fn generic_arg() { run_and_expect_no_errors("test_data/parser/inline/ok/generic_arg.rs"); }
+    #[test]
+    fn generic_param_attribute() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_attribute.rs");
+    }
+    #[test]
+    fn generic_param_list() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_list.rs");
+    }
+    #[test]
+    fn half_open_range_pat() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs");
+    }
+    #[test]
+    fn if_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/if_expr.rs"); }
+    #[test]
+    fn impl_item() { run_and_expect_no_errors("test_data/parser/inline/ok/impl_item.rs"); }
+    #[test]
+    fn impl_item_const() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/impl_item_const.rs");
+    }
+    #[test]
+    fn impl_item_neg() { run_and_expect_no_errors("test_data/parser/inline/ok/impl_item_neg.rs"); }
+    #[test]
+    fn impl_trait_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/impl_trait_type.rs");
+    }
+    #[test]
+    fn impl_type_params() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/impl_type_params.rs");
+    }
+    #[test]
+    fn index_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/index_expr.rs"); }
+    #[test]
+    fn label() { run_and_expect_no_errors("test_data/parser/inline/ok/label.rs"); }
+    #[test]
+    fn labeled_block() { run_and_expect_no_errors("test_data/parser/inline/ok/labeled_block.rs"); }
+    #[test]
+    fn lambda_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/lambda_expr.rs"); }
+    #[test]
+    fn lambda_ret_block() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/lambda_ret_block.rs");
+    }
+    #[test]
+    fn let_else() { run_and_expect_no_errors("test_data/parser/inline/ok/let_else.rs"); }
+    #[test]
+    fn let_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/let_expr.rs"); }
+    #[test]
+    fn let_stmt() { run_and_expect_no_errors("test_data/parser/inline/ok/let_stmt.rs"); }
+    #[test]
+    fn let_stmt_ascription() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/let_stmt_ascription.rs");
+    }
+    #[test]
+    fn let_stmt_init() { run_and_expect_no_errors("test_data/parser/inline/ok/let_stmt_init.rs"); }
+    #[test]
+    fn lifetime_arg() { run_and_expect_no_errors("test_data/parser/inline/ok/lifetime_arg.rs"); }
+    #[test]
+    fn lifetime_param() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/lifetime_param.rs");
+    }
+    #[test]
+    fn literal_pattern() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/literal_pattern.rs");
+    }
+    #[test]
+    fn loop_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/loop_expr.rs"); }
+    #[test]
+    fn macro_call_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/macro_call_type.rs");
+    }
+    #[test]
+    fn macro_def() { run_and_expect_no_errors("test_data/parser/inline/ok/macro_def.rs"); }
+    #[test]
+    fn macro_def_curly() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/macro_def_curly.rs");
+    }
+    #[test]
+    fn macro_inside_generic_arg() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/macro_inside_generic_arg.rs");
+    }
+    #[test]
+    fn macro_rules_as_macro_name() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/macro_rules_as_macro_name.rs");
+    }
+    #[test]
+    fn macro_rules_non_brace() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/macro_rules_non_brace.rs");
+    }
+    #[test]
+    fn marco_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/marco_pat.rs"); }
+    #[test]
+    fn match_arm() { run_and_expect_no_errors("test_data/parser/inline/ok/match_arm.rs"); }
+    #[test]
+    fn match_arms_commas() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/match_arms_commas.rs");
+    }
+    #[test]
+    fn match_arms_inner_attribute() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/match_arms_inner_attribute.rs");
+    }
+    #[test]
+    fn match_arms_outer_attributes() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/match_arms_outer_attributes.rs");
+    }
+    #[test]
+    fn match_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/match_expr.rs"); }
+    #[test]
+    fn match_guard() { run_and_expect_no_errors("test_data/parser/inline/ok/match_guard.rs"); }
+    #[test]
+    fn metas() { run_and_expect_no_errors("test_data/parser/inline/ok/metas.rs"); }
+    #[test]
+    fn method_call_expr() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/method_call_expr.rs");
+    }
+    #[test]
+    fn mod_contents() { run_and_expect_no_errors("test_data/parser/inline/ok/mod_contents.rs"); }
+    #[test]
+    fn mod_item() { run_and_expect_no_errors("test_data/parser/inline/ok/mod_item.rs"); }
+    #[test]
+    fn mod_item_curly() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/mod_item_curly.rs");
+    }
+    #[test]
+    fn never_type() { run_and_expect_no_errors("test_data/parser/inline/ok/never_type.rs"); }
+    #[test]
+    fn no_dyn_trait_leading_for() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/no_dyn_trait_leading_for.rs");
+    }
+    #[test]
+    fn no_semi_after_block() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/no_semi_after_block.rs");
+    }
+    #[test]
+    fn nocontentexpr() { run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr.rs"); }
+    #[test]
+    fn nocontentexpr_after_item() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs");
+    }
+    #[test]
+    fn or_pattern() { run_and_expect_no_errors("test_data/parser/inline/ok/or_pattern.rs"); }
+    #[test]
+    fn param_list() { run_and_expect_no_errors("test_data/parser/inline/ok/param_list.rs"); }
+    #[test]
+    fn param_list_opt_patterns() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/param_list_opt_patterns.rs");
+    }
+    #[test]
+    fn param_list_vararg() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/param_list_vararg.rs");
+    }
+    #[test]
+    fn param_outer_arg() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/param_outer_arg.rs");
+    }
+    #[test]
+    fn paren_type() { run_and_expect_no_errors("test_data/parser/inline/ok/paren_type.rs"); }
+    #[test]
+    fn path_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/path_expr.rs"); }
+    #[test]
+    fn path_fn_trait_args() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/path_fn_trait_args.rs");
+    }
+    #[test]
+    fn path_part() { run_and_expect_no_errors("test_data/parser/inline/ok/path_part.rs"); }
+    #[test]
+    fn path_type() { run_and_expect_no_errors("test_data/parser/inline/ok/path_type.rs"); }
+    #[test]
+    fn path_type_with_bounds() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/path_type_with_bounds.rs");
+    }
+    #[test]
+    fn placeholder_pat() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/placeholder_pat.rs");
+    }
+    #[test]
+    fn placeholder_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/placeholder_type.rs");
+    }
+    #[test]
+    fn pointer_type_mut() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/pointer_type_mut.rs");
+    }
+    #[test]
+    fn postfix_range() { run_and_expect_no_errors("test_data/parser/inline/ok/postfix_range.rs"); }
+    #[test]
+    fn precise_capturing() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/precise_capturing.rs");
+    }
+    #[test]
+    fn pub_parens_typepath() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/pub_parens_typepath.rs");
+    }
+    #[test]
+    fn pub_tuple_field() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/pub_tuple_field.rs");
+    }
+    #[test]
+    fn qual_paths() { run_and_expect_no_errors("test_data/parser/inline/ok/qual_paths.rs"); }
+    #[test]
+    fn question_for_type_trait_bound() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/question_for_type_trait_bound.rs");
+    }
+    #[test]
+    fn range_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/range_pat.rs"); }
+    #[test]
+    fn record_field_attrs() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs");
+    }
+    #[test]
+    fn record_field_list() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs");
+    }
+    #[test]
+    fn record_lit() { run_and_expect_no_errors("test_data/parser/inline/ok/record_lit.rs"); }
+    #[test]
+    fn record_literal_field_with_attr() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_literal_field_with_attr.rs");
+    }
+    #[test]
+    fn record_pat_field() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_pat_field.rs");
+    }
+    #[test]
+    fn record_pat_field_list() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_pat_field_list.rs");
+    }
+    #[test]
+    fn ref_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/ref_expr.rs"); }
+    #[test]
+    fn ref_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/ref_pat.rs"); }
+    #[test]
+    fn reference_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/reference_type.rs");
+    }
+    #[test]
+    fn return_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_expr.rs"); }
+    #[test]
+    fn self_param() { run_and_expect_no_errors("test_data/parser/inline/ok/self_param.rs"); }
+    #[test]
+    fn self_param_outer_attr() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/self_param_outer_attr.rs");
+    }
+    #[test]
+    fn singleton_tuple_type() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/singleton_tuple_type.rs");
+    }
+    #[test]
+    fn slice_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/slice_pat.rs"); }
+    #[test]
+    fn slice_type() { run_and_expect_no_errors("test_data/parser/inline/ok/slice_type.rs"); }
+    #[test]
+    fn stmt_bin_expr_ambiguity() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/stmt_bin_expr_ambiguity.rs");
+    }
+    #[test]
+    fn stmt_postfix_expr_ambiguity() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs");
+    }
+    #[test]
+    fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); }
+    #[test]
+    fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); }
+    #[test]
+    fn trait_alias_where_clause() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias_where_clause.rs");
+    }
+    #[test]
+    fn trait_item() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_item.rs"); }
+    #[test]
+    fn trait_item_bounds() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/trait_item_bounds.rs");
+    }
+    #[test]
+    fn trait_item_generic_params() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/trait_item_generic_params.rs");
+    }
+    #[test]
+    fn trait_item_where_clause() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/trait_item_where_clause.rs");
+    }
+    #[test]
+    fn try_block_expr() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/try_block_expr.rs");
+    }
+    #[test]
+    fn try_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/try_expr.rs"); }
+    #[test]
+    fn try_macro_fallback() {
+        run_and_expect_no_errors_with_edition(
+            "test_data/parser/inline/ok/try_macro_fallback.rs",
+            crate::Edition::Edition2015,
+        );
+    }
+    #[test]
+    fn try_macro_rules() {
+        run_and_expect_no_errors_with_edition(
+            "test_data/parser/inline/ok/try_macro_rules.rs",
+            crate::Edition::Edition2015,
+        );
+    }
+    #[test]
+    fn tuple_attrs() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_attrs.rs"); }
+    #[test]
+    fn tuple_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_expr.rs"); }
+    #[test]
+    fn tuple_field_attrs() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/tuple_field_attrs.rs");
+    }
+    #[test]
+    fn tuple_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_pat.rs"); }
+    #[test]
+    fn tuple_pat_fields() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/tuple_pat_fields.rs");
+    }
+    #[test]
+    fn tuple_struct() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_struct.rs"); }
+    #[test]
+    fn tuple_struct_where() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/tuple_struct_where.rs");
+    }
+    #[test]
+    fn type_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/type_alias.rs"); }
+    #[test]
+    fn type_item_type_params() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/type_item_type_params.rs");
+    }
+    #[test]
+    fn type_item_where_clause() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/type_item_where_clause.rs");
+    }
+    #[test]
+    fn type_item_where_clause_deprecated() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/type_item_where_clause_deprecated.rs");
+    }
+    #[test]
+    fn type_param() { run_and_expect_no_errors("test_data/parser/inline/ok/type_param.rs"); }
+    #[test]
+    fn type_param_bounds() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/type_param_bounds.rs");
+    }
+    #[test]
+    fn type_param_default() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/type_param_default.rs");
+    }
+    #[test]
+    fn type_path_in_pattern() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/type_path_in_pattern.rs");
+    }
+    #[test]
+    fn typepathfn_with_coloncolon() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/typepathfn_with_coloncolon.rs");
+    }
+    #[test]
+    fn unary_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/unary_expr.rs"); }
+    #[test]
+    fn union_item() { run_and_expect_no_errors("test_data/parser/inline/ok/union_item.rs"); }
+    #[test]
+    fn unit_struct() { run_and_expect_no_errors("test_data/parser/inline/ok/unit_struct.rs"); }
+    #[test]
+    fn unit_type() { run_and_expect_no_errors("test_data/parser/inline/ok/unit_type.rs"); }
+    #[test]
+    fn use_item() { run_and_expect_no_errors("test_data/parser/inline/ok/use_item.rs"); }
+    #[test]
+    fn use_tree() { run_and_expect_no_errors("test_data/parser/inline/ok/use_tree.rs"); }
+    #[test]
+    fn use_tree_abs_star() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_abs_star.rs");
+    }
+    #[test]
+    fn use_tree_alias() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_alias.rs");
+    }
+    #[test]
+    fn use_tree_list() { run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_list.rs"); }
+    #[test]
+    fn use_tree_path() { run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_path.rs"); }
+    #[test]
+    fn use_tree_path_star() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_path_star.rs");
+    }
+    #[test]
+    fn use_tree_path_use_tree() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_path_use_tree.rs");
+    }
+    #[test]
+    fn use_tree_star() { run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_star.rs"); }
+    #[test]
+    fn value_parameters_no_patterns() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/value_parameters_no_patterns.rs");
+    }
+    #[test]
+    fn variant_discriminant() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/variant_discriminant.rs");
+    }
+    #[test]
+    fn where_clause() { run_and_expect_no_errors("test_data/parser/inline/ok/where_clause.rs"); }
+    #[test]
+    fn where_pred_for() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/where_pred_for.rs");
+    }
+    #[test]
+    fn while_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/while_expr.rs"); }
+    #[test]
+    fn yeet_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/yeet_expr.rs"); }
+    #[test]
+    fn yield_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/yield_expr.rs"); }
+}
+mod err {
+    use crate::tests::*;
+    #[test]
+    fn angled_path_without_qual() {
+        run_and_expect_errors("test_data/parser/inline/err/angled_path_without_qual.rs");
+    }
+    #[test]
+    fn anonymous_static() {
+        run_and_expect_errors("test_data/parser/inline/err/anonymous_static.rs");
+    }
+    #[test]
+    fn arg_list_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/arg_list_recovery.rs");
+    }
+    #[test]
+    fn array_type_missing_semi() {
+        run_and_expect_errors("test_data/parser/inline/err/array_type_missing_semi.rs");
+    }
+    #[test]
+    fn async_without_semicolon() {
+        run_and_expect_errors("test_data/parser/inline/err/async_without_semicolon.rs");
+    }
+    #[test]
+    fn comma_after_functional_update_syntax() {
+        run_and_expect_errors(
+            "test_data/parser/inline/err/comma_after_functional_update_syntax.rs",
+        );
+    }
+    #[test]
+    fn crate_visibility_empty_recover() {
+        run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs");
+    }
+    #[test]
+    fn empty_param_slot() {
+        run_and_expect_errors("test_data/parser/inline/err/empty_param_slot.rs");
+    }
+    #[test]
+    fn empty_segment() { run_and_expect_errors("test_data/parser/inline/err/empty_segment.rs"); }
+    #[test]
+    fn fn_pointer_type_missing_fn() {
+        run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs");
+    }
+    #[test]
+    fn gen_fn() { run_and_expect_errors("test_data/parser/inline/err/gen_fn.rs"); }
+    #[test]
+    fn generic_arg_list_recover() {
+        run_and_expect_errors("test_data/parser/inline/err/generic_arg_list_recover.rs");
+    }
+    #[test]
+    fn generic_param_list_recover() {
+        run_and_expect_errors("test_data/parser/inline/err/generic_param_list_recover.rs");
+    }
+    #[test]
+    fn impl_type() { run_and_expect_errors("test_data/parser/inline/err/impl_type.rs"); }
+    #[test]
+    fn let_else_right_curly_brace() {
+        run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs");
+    }
+    #[test]
+    fn macro_rules_as_macro_name() {
+        run_and_expect_errors("test_data/parser/inline/err/macro_rules_as_macro_name.rs");
+    }
+    #[test]
+    fn match_arms_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/match_arms_recovery.rs");
+    }
+    #[test]
+    fn method_call_missing_argument_list() {
+        run_and_expect_errors("test_data/parser/inline/err/method_call_missing_argument_list.rs");
+    }
+    #[test]
+    fn misplaced_label_err() {
+        run_and_expect_errors("test_data/parser/inline/err/misplaced_label_err.rs");
+    }
+    #[test]
+    fn missing_fn_param_type() {
+        run_and_expect_errors("test_data/parser/inline/err/missing_fn_param_type.rs");
+    }
+    #[test]
+    fn pointer_type_no_mutability() {
+        run_and_expect_errors("test_data/parser/inline/err/pointer_type_no_mutability.rs");
+    }
+    #[test]
+    fn pub_expr() { run_and_expect_errors("test_data/parser/inline/err/pub_expr.rs"); }
+    #[test]
+    fn record_literal_before_ellipsis_recovery() {
+        run_and_expect_errors(
+            "test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs",
+        );
+    }
+    #[test]
+    fn record_literal_field_eq_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/record_literal_field_eq_recovery.rs");
+    }
+    #[test]
+    fn record_literal_missing_ellipsis_recovery() {
+        run_and_expect_errors(
+            "test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs",
+        );
+    }
+    #[test]
+    fn record_pat_field_eq_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/record_pat_field_eq_recovery.rs");
+    }
+    #[test]
+    fn recover_from_missing_assoc_item_binding() {
+        run_and_expect_errors(
+            "test_data/parser/inline/err/recover_from_missing_assoc_item_binding.rs",
+        );
+    }
+    #[test]
+    fn recover_from_missing_const_default() {
+        run_and_expect_errors("test_data/parser/inline/err/recover_from_missing_const_default.rs");
+    }
+    #[test]
+    fn struct_field_recover() {
+        run_and_expect_errors("test_data/parser/inline/err/struct_field_recover.rs");
+    }
+    #[test]
+    fn top_level_let() { run_and_expect_errors("test_data/parser/inline/err/top_level_let.rs"); }
+    #[test]
+    fn tuple_expr_leading_comma() {
+        run_and_expect_errors("test_data/parser/inline/err/tuple_expr_leading_comma.rs");
+    }
+    #[test]
+    fn tuple_field_list_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/tuple_field_list_recovery.rs");
+    }
+    #[test]
+    fn tuple_pat_leading_comma() {
+        run_and_expect_errors("test_data/parser/inline/err/tuple_pat_leading_comma.rs");
+    }
+    #[test]
+    fn unsafe_block_in_mod() {
+        run_and_expect_errors("test_data/parser/inline/err/unsafe_block_in_mod.rs");
+    }
+    #[test]
+    fn use_tree_list_err_recovery() {
+        run_and_expect_errors("test_data/parser/inline/err/use_tree_list_err_recovery.rs");
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0011_extern_struct.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0011_extern_struct.rast
index bd5ec4b7c291e..249bfeeeeee30 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0011_extern_struct.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0011_extern_struct.rast
@@ -10,4 +10,4 @@ SOURCE_FILE
       IDENT "Foo"
     SEMICOLON ";"
   WHITESPACE "\n"
-error 6: expected existential, fn, trait or impl
+error 6: expected fn, trait or impl
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast
index 1cdc6e6e71927..d6d2e75cca675 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast
@@ -69,7 +69,7 @@ SOURCE_FILE
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
-error 24: expected existential, fn, trait or impl
-error 41: expected existential, fn, trait or impl
+error 24: expected fn, trait or impl
+error 41: expected fn, trait or impl
 error 56: expected a block
 error 75: expected a loop or block
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast
index 96e471a69a781..76464bf7cc272 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0044_item_modifiers.rast
@@ -39,7 +39,7 @@ SOURCE_FILE
     L_CURLY "{"
     R_CURLY "}"
   WHITESPACE "\n"
-error 6: expected existential, fn, trait or impl
+error 6: expected fn, trait or impl
 error 38: expected a name
 error 40: missing type for `const` or `static`
 error 40: expected SEMICOLON
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
index 4b2a740362ed1..a56d692335fe9 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
@@ -11,5 +11,5 @@ SOURCE_FILE
       WHITESPACE " "
       STRING "\"C\""
   WHITESPACE "\n"
-error 10: expected existential, fn, trait or impl
-error 21: expected existential, fn, trait or impl
+error 10: expected fn, trait or impl
+error 21: expected fn, trait or impl
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/angled_path_without_qual.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0013_anonymous_static.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/anonymous_static.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0013_anonymous_static.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/anonymous_static.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0013_anonymous_static.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/anonymous_static.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0013_anonymous_static.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/anonymous_static.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/arg_list_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/arg_list_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/arg_list_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_arg_list_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/arg_list_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0001_array_type_missing_semi.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/array_type_missing_semi.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0001_array_type_missing_semi.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/array_type_missing_semi.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0001_array_type_missing_semi.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/array_type_missing_semi.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0001_array_type_missing_semi.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/array_type_missing_semi.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0007_async_without_semicolon.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/async_without_semicolon.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0007_async_without_semicolon.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/async_without_semicolon.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0007_async_without_semicolon.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/async_without_semicolon.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0007_async_without_semicolon.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/async_without_semicolon.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_functional_update_syntax.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_functional_update_syntax.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_functional_update_syntax.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_functional_update_syntax.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_param_slot.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_param_slot.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_param_slot.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0023_empty_param_slot.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_param_slot.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_empty_segment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_segment.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_empty_segment.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_segment.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_empty_segment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_segment.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_empty_segment.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/empty_segment.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/fn_pointer_type_missing_fn.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/fn_pointer_type_missing_fn.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/fn_pointer_type_missing_fn.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0005_fn_pointer_type_missing_fn.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/fn_pointer_type_missing_fn.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/gen_fn.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/gen_fn.rast
new file mode 100644
index 0000000000000..9609ece77dfaa
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/gen_fn.rast
@@ -0,0 +1,51 @@
+SOURCE_FILE
+  MACRO_CALL
+    PATH
+      PATH_SEGMENT
+        NAME_REF
+          IDENT "gen"
+  WHITESPACE " "
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "gen_fn"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
+  ERROR
+    ASYNC_KW "async"
+  WHITESPACE " "
+  MACRO_CALL
+    PATH
+      PATH_SEGMENT
+        NAME_REF
+          IDENT "gen"
+  WHITESPACE " "
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "async_gen_fn"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 3: expected BANG
+error 3: expected `{`, `[`, `(`
+error 3: expected SEMICOLON
+error 24: expected fn, trait or impl
+error 28: expected BANG
+error 28: expected `{`, `[`, `(`
+error 28: expected SEMICOLON
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/gen_fn.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/gen_fn.rs
new file mode 100644
index 0000000000000..80882e0a4044a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/gen_fn.rs
@@ -0,0 +1,2 @@
+gen fn gen_fn() {}
+async gen fn async_gen_fn() {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0030_generic_arg_list_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0030_generic_arg_list_recover.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0030_generic_arg_list_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0030_generic_arg_list_recover.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0031_generic_param_list_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_param_list_recover.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0031_generic_param_list_recover.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_param_list_recover.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0031_generic_param_list_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_param_list_recover.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0031_generic_param_list_recover.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/generic_param_list_recover.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0004_impl_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/impl_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0004_impl_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/impl_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0004_impl_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/impl_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0004_impl_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/impl_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/let_else_right_curly_brace.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/let_else_right_curly_brace.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/let_else_right_curly_brace.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/let_else_right_curly_brace.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_macro_rules_as_macro_name.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_rules_as_macro_name.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_macro_rules_as_macro_name.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_rules_as_macro_name.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_macro_rules_as_macro_name.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_rules_as_macro_name.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_macro_rules_as_macro_name.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/macro_rules_as_macro_name.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/match_arms_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/match_arms_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/match_arms_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/match_arms_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0028_method_call_missing_argument_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/method_call_missing_argument_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0028_method_call_missing_argument_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/method_call_missing_argument_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0028_method_call_missing_argument_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/method_call_missing_argument_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0028_method_call_missing_argument_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/method_call_missing_argument_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/misplaced_label_err.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/misplaced_label_err.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/misplaced_label_err.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0002_misplaced_label_err.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/misplaced_label_err.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_fn_param_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_fn_param_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_fn_param_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0015_missing_fn_param_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/missing_fn_param_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0003_pointer_type_no_mutability.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pointer_type_no_mutability.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0003_pointer_type_no_mutability.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pointer_type_no_mutability.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0003_pointer_type_no_mutability.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pointer_type_no_mutability.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0003_pointer_type_no_mutability.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pointer_type_no_mutability.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0008_pub_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pub_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0008_pub_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pub_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0008_pub_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pub_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0008_pub_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/pub_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0032_record_literal_field_eq_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0032_record_literal_field_eq_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0032_record_literal_field_eq_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0032_record_literal_field_eq_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0033_record_pat_field_eq_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_pat_field_eq_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0033_record_pat_field_eq_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_pat_field_eq_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0033_record_pat_field_eq_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_pat_field_eq_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0033_record_pat_field_eq_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_pat_field_eq_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_assoc_item_binding.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_assoc_item_binding.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_assoc_item_binding.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_assoc_item_binding.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_const_default.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_const_default.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_const_default.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/recover_from_missing_const_default.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_struct_field_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/struct_field_recover.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_struct_field_recover.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/struct_field_recover.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_struct_field_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/struct_field_recover.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0014_struct_field_recover.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/struct_field_recover.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/top_level_let.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/top_level_let.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/top_level_let.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/top_level_let.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0029_tuple_field_list_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_field_list_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0029_tuple_field_list_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_field_list_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0029_tuple_field_list_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_field_list_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0029_tuple_field_list_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_field_list_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_pat_leading_comma.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_pat_leading_comma.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_pat_leading_comma.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_pat_leading_comma.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0006_unsafe_block_in_mod.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/unsafe_block_in_mod.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0006_unsafe_block_in_mod.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/unsafe_block_in_mod.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0006_unsafe_block_in_mod.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/unsafe_block_in_mod.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0006_unsafe_block_in_mod.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/unsafe_block_in_mod.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/use_tree_list_err_recovery.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/use_tree_list_err_recovery.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/use_tree_list_err_recovery.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0026_use_tree_list_err_recovery.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/use_tree_list_err_recovery.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0083_struct_items.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0083_struct_items.rast
deleted file mode 100644
index cdbc40fe0b2fb..0000000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0083_struct_items.rast
+++ /dev/null
@@ -1,87 +0,0 @@
-SOURCE_FILE@0..106
-  STRUCT@0..11
-    STRUCT_KW@0..6 "struct"
-    WHITESPACE@6..7 " "
-    NAME@7..10
-      IDENT@7..10 "Foo"
-    SEMICOLON@10..11 ";"
-  WHITESPACE@11..12 "\n"
-  STRUCT@12..25
-    STRUCT_KW@12..18 "struct"
-    WHITESPACE@18..19 " "
-    NAME@19..22
-      IDENT@19..22 "Foo"
-    WHITESPACE@22..23 " "
-    RECORD_FIELD_LIST@23..25
-      L_CURLY@23..24 "{"
-      R_CURLY@24..25 "}"
-  WHITESPACE@25..26 "\n"
-  STRUCT@26..39
-    STRUCT_KW@26..32 "struct"
-    WHITESPACE@32..33 " "
-    NAME@33..36
-      IDENT@33..36 "Foo"
-    TUPLE_FIELD_LIST@36..38
-      L_PAREN@36..37 "("
-      R_PAREN@37..38 ")"
-    SEMICOLON@38..39 ";"
-  WHITESPACE@39..40 "\n"
-  STRUCT@40..66
-    STRUCT_KW@40..46 "struct"
-    WHITESPACE@46..47 " "
-    NAME@47..50
-      IDENT@47..50 "Foo"
-    TUPLE_FIELD_LIST@50..65
-      L_PAREN@50..51 "("
-      TUPLE_FIELD@51..57
-        PATH_TYPE@51..57
-          PATH@51..57
-            PATH_SEGMENT@51..57
-              NAME_REF@51..57
-                IDENT@51..57 "String"
-      COMMA@57..58 ","
-      WHITESPACE@58..59 " "
-      TUPLE_FIELD@59..64
-        PATH_TYPE@59..64
-          PATH@59..64
-            PATH_SEGMENT@59..64
-              NAME_REF@59..64
-                IDENT@59..64 "usize"
-      R_PAREN@64..65 ")"
-    SEMICOLON@65..66 ";"
-  WHITESPACE@66..67 "\n"
-  STRUCT@67..105
-    STRUCT_KW@67..73 "struct"
-    WHITESPACE@73..74 " "
-    NAME@74..77
-      IDENT@74..77 "Foo"
-    WHITESPACE@77..78 " "
-    RECORD_FIELD_LIST@78..105
-      L_CURLY@78..79 "{"
-      WHITESPACE@79..84 "\n    "
-      RECORD_FIELD@84..90
-        NAME@84..85
-          IDENT@84..85 "a"
-        COLON@85..86 ":"
-        WHITESPACE@86..87 " "
-        PATH_TYPE@87..90
-          PATH@87..90
-            PATH_SEGMENT@87..90
-              NAME_REF@87..90
-                IDENT@87..90 "i32"
-      COMMA@90..91 ","
-      WHITESPACE@91..96 "\n    "
-      RECORD_FIELD@96..102
-        NAME@96..97
-          IDENT@96..97 "b"
-        COLON@97..98 ":"
-        WHITESPACE@98..99 " "
-        PATH_TYPE@99..102
-          PATH@99..102
-            PATH_SEGMENT@99..102
-              NAME_REF@99..102
-                IDENT@99..102 "f32"
-      COMMA@102..103 ","
-      WHITESPACE@103..104 "\n"
-      R_CURLY@104..105 "}"
-  WHITESPACE@105..106 "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0131_existential_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0131_existential_type.rast
deleted file mode 100644
index b73780261baa2..0000000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0131_existential_type.rast
+++ /dev/null
@@ -1,31 +0,0 @@
-SOURCE_FILE
-  TYPE_ALIAS
-    EXISTENTIAL_KW "existential"
-    WHITESPACE " "
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "Foo"
-    COLON ":"
-    WHITESPACE " "
-    TYPE_BOUND_LIST
-      TYPE_BOUND
-        PATH_TYPE
-          PATH
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "Fn"
-              PARAM_LIST
-                L_PAREN "("
-                R_PAREN ")"
-              WHITESPACE " "
-              RET_TYPE
-                THIN_ARROW "->"
-                WHITESPACE " "
-                PATH_TYPE
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "usize"
-    SEMICOLON ";"
-  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0131_existential_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0131_existential_type.rs
deleted file mode 100644
index 23baf7145cccb..0000000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0131_existential_type.rs
+++ /dev/null
@@ -1 +0,0 @@
-existential type Foo: Fn() -> usize;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_anonymous_const.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/anonymous_const.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_anonymous_const.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/anonymous_const.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_anonymous_const.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/anonymous_const.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_anonymous_const.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/anonymous_const.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0018_arb_self_types.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arb_self_types.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0018_arb_self_types.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arb_self_types.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0018_arb_self_types.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arb_self_types.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0018_arb_self_types.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arb_self_types.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0152_arg_with_attr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0152_arg_with_attr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0152_arg_with_attr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arg_with_attr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0152_arg_with_attr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/arg_with_attr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_array_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_attrs.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_array_attrs.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_attrs.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_array_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_attrs.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_array_attrs.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_attrs.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0103_array_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0103_array_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0103_array_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0103_array_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/array_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0146_as_precedence.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/as_precedence.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0146_as_precedence.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/as_precedence.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0146_as_precedence.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/as_precedence.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0146_as_precedence.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/as_precedence.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_assoc_const_eq.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_const_eq.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_assoc_const_eq.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_const_eq.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_assoc_const_eq.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_const_eq.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_assoc_const_eq.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_const_eq.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0021_assoc_item_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0021_assoc_item_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0021_assoc_item_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0021_assoc_item_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_assoc_item_list_inner_attrs.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0185_assoc_type_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0185_assoc_type_bound.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0185_assoc_type_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0185_assoc_type_bound.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0187_assoc_type_eq.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_eq.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0187_assoc_type_eq.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_eq.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0187_assoc_type_eq.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_eq.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0187_assoc_type_eq.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/assoc_type_eq.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_associated_type_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_associated_type_bounds.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_associated_type_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_associated_type_bounds.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0211_async_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/async_trait_bound.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0211_async_trait_bound.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/async_trait_bound.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0211_async_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/async_trait_bound.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0211_async_trait_bound.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/async_trait_bound.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0137_await_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/await_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0137_await_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/await_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0137_await_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/await_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0137_await_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/await_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_leading_lifetime.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_leading_lifetime.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_leading_lifetime.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_leading_lifetime.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_become_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/become_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_become_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/become_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_become_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/become_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_become_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/become_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0112_bind_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bind_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0112_bind_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bind_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0112_bind_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bind_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0112_bind_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/bind_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_binop_resets_statementness.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/binop_resets_statementness.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_binop_resets_statementness.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/binop_resets_statementness.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_binop_resets_statementness.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/binop_resets_statementness.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_binop_resets_statementness.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/binop_resets_statementness.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0075_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0075_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0075_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0075_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0044_block_items.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block_items.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0044_block_items.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block_items.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0044_block_items.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block_items.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0044_block_items.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/block_items.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0143_box_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/box_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0143_box_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/box_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0143_box_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/box_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0143_box_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/box_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_ambiguity.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_ambiguity.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_ambiguity.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_ambiguity.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0034_break_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0034_break_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0034_break_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0034_break_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/break_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0042_call_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0042_call_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0042_call_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0042_call_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/call_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0029_cast_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cast_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0029_cast_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cast_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0029_cast_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cast_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0029_cast_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cast_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0203_closure_body_underscore_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_body_underscore_assignment.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0203_closure_body_underscore_assignment.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_body_underscore_assignment.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0203_closure_body_underscore_assignment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_body_underscore_assignment.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0203_closure_body_underscore_assignment.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_body_underscore_assignment.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0155_closure_params.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_params.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0155_closure_params.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_params.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0155_closure_params.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_params.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0155_closure_params.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_params.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_range_method_call.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_range_method_call.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_range_method_call.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_closure_range_method_call.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_range_method_call.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_const_arg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_const_arg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_const_arg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_const_arg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_const_arg_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_const_arg_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_const_arg_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_const_arg_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0192_const_arg_bool_literal.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_bool_literal.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0192_const_arg_bool_literal.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_bool_literal.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0192_const_arg_bool_literal.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_bool_literal.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0192_const_arg_bool_literal.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_bool_literal.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0189_const_arg_literal.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_literal.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0189_const_arg_literal.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_literal.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0189_const_arg_literal.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_literal.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0189_const_arg_literal.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_literal.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0191_const_arg_negative_number.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_negative_number.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0191_const_arg_negative_number.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_negative_number.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0191_const_arg_negative_number.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_negative_number.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0191_const_arg_negative_number.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_arg_negative_number.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_block_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_block_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_block_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_block_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0205_const_closure.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_closure.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0205_const_closure.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_closure.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0205_const_closure.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_closure.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0205_const_closure.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_closure.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_const_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_const_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_const_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_const_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_const_param.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_const_param.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_const_param.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_const_param.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_expression.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_expression.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_expression.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_expression.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_literal.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_literal.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_literal.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_literal.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_path.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_path.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_path.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_param_default_path.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0212_const_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_trait_bound.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0212_const_trait_bound.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_trait_bound.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0212_const_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_trait_bound.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0212_const_trait_bound.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/const_trait_bound.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0015_continue_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/continue_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0015_continue_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/continue_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0015_continue_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/continue_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0015_continue_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/continue_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0067_crate_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_path.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0067_crate_path.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_path.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0067_crate_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_path.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0067_crate_path.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_path.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0022_crate_visibility.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0022_crate_visibility.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0022_crate_visibility.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0022_crate_visibility.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_crate_visibility_in.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_crate_visibility_in.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_crate_visibility_in.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_crate_visibility_in.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_async_fn.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_fn.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_async_fn.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_fn.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_async_fn.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_fn.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_async_fn.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_fn.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0162_default_async_unsafe_fn.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_unsafe_fn.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0162_default_async_unsafe_fn.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_unsafe_fn.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0162_default_async_unsafe_fn.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_unsafe_fn.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0162_default_async_unsafe_fn.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_async_unsafe_fn.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_default_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_default_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_default_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_default_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_unsafe_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_unsafe_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_unsafe_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_unsafe_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_unsafe_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_unsafe_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0163_default_unsafe_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/default_unsafe_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0197_destructuring_assignment_struct_rest_pattern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0197_destructuring_assignment_struct_rest_pattern.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0197_destructuring_assignment_struct_rest_pattern.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0197_destructuring_assignment_struct_rest_pattern.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0198_destructuring_assignment_wildcard_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_wildcard_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0198_destructuring_assignment_wildcard_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_wildcard_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0198_destructuring_assignment_wildcard_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_wildcard_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0198_destructuring_assignment_wildcard_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/destructuring_assignment_wildcard_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0144_dot_dot_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dot_dot_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0144_dot_dot_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dot_dot_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0144_dot_dot_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dot_dot_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0144_dot_dot_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dot_dot_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0065_dyn_trait_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0065_dyn_trait_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0065_dyn_trait_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0065_dyn_trait_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
new file mode 100644
index 0000000000000..dcc66dc1e2b67
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
@@ -0,0 +1,186 @@
+SOURCE_FILE
+  TYPE_ALIAS
+    COMMENT "// 2015"
+    WHITESPACE "\n"
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "DynPlain"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    DYN_TRAIT_TYPE
+      DYN_KW "dyn"
+      WHITESPACE " "
+      TYPE_BOUND_LIST
+        TYPE_BOUND
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "Path"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "DynRef"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    REF_TYPE
+      AMP "&"
+      DYN_TRAIT_TYPE
+        DYN_KW "dyn"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Path"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "DynLt"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    DYN_TRAIT_TYPE
+      DYN_KW "dyn"
+      WHITESPACE " "
+      TYPE_BOUND_LIST
+        TYPE_BOUND
+          LIFETIME
+            LIFETIME_IDENT "'a"
+        WHITESPACE " "
+        PLUS "+"
+        WHITESPACE " "
+        TYPE_BOUND
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "Path"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "DynQuestion"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    DYN_TRAIT_TYPE
+      DYN_KW "dyn"
+      WHITESPACE " "
+      TYPE_BOUND_LIST
+        TYPE_BOUND
+          QUESTION "?"
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "Path"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "DynFor"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    DYN_TRAIT_TYPE
+      DYN_KW "dyn"
+      WHITESPACE " "
+      TYPE_BOUND_LIST
+        TYPE_BOUND
+          FOR_TYPE
+            FOR_KW "for"
+            GENERIC_PARAM_LIST
+              L_ANGLE "<"
+              LIFETIME_PARAM
+                LIFETIME
+                  LIFETIME_IDENT "'a"
+              R_ANGLE ">"
+            WHITESPACE " "
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Path"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "DynParen"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    DYN_TRAIT_TYPE
+      DYN_KW "dyn"
+      TYPE_BOUND_LIST
+        TYPE_BOUND
+          L_PAREN "("
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "Path"
+          R_PAREN ")"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "Path"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "dyn"
+        COLON2 "::"
+        PATH_SEGMENT
+          NAME_REF
+            IDENT "Path"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "Generic"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH_SEGMENT
+          NAME_REF
+            IDENT "dyn"
+          GENERIC_ARG_LIST
+            L_ANGLE "<"
+            TYPE_ARG
+              PATH_TYPE
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "Path"
+            R_ANGLE ">"
+    SEMICOLON ";"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
new file mode 100644
index 0000000000000..c4ef1f2b7ac02
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
@@ -0,0 +1,9 @@
+// 2015
+type DynPlain = dyn Path;
+type DynRef = &dyn Path;
+type DynLt = dyn 'a + Path;
+type DynQuestion = dyn ?Path;
+type DynFor = dyn for<'a> Path;
+type DynParen = dyn(Path);
+type Path = dyn::Path;
+type Generic = dyn;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/effect_blocks.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/effect_blocks.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/effect_blocks.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_effect_blocks.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/effect_blocks.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/exclusive_range_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/exclusive_range_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/exclusive_range_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/exclusive_range_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expr_literals.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expr_literals.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expr_literals.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expr_literals.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_expression_after_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expression_after_block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_expression_after_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expression_after_block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_expression_after_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expression_after_block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_expression_after_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/expression_after_block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0010_extern_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0010_extern_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0010_extern_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0010_extern_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0060_extern_crate.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0060_extern_crate.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0060_extern_crate.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0060_extern_crate.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_rename.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_rename.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_rename.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_rename.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_self.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_self.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_self.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_self.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_self.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_self.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0168_extern_crate_self.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/extern_crate_self.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0011_field_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0011_field_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0011_field_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0011_field_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/field_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_fn.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_fn.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_fn.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_fn.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0050_fn_decl.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_decl.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0050_fn_decl.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_decl.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0050_fn_decl.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_decl.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0050_fn_decl.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_decl.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_fn_def_param.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_def_param.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_fn_def_param.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_def_param.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_fn_def_param.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_def_param.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_fn_def_param.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_def_param.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_fn_pointer_param_ident_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_param_ident_path.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_fn_pointer_param_ident_path.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_param_ident_path.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_fn_pointer_param_ident_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_param_ident_path.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_fn_pointer_param_ident_path.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_param_ident_path.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0032_fn_pointer_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0032_fn_pointer_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0032_fn_pointer_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0032_fn_pointer_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0092_fn_pointer_type_with_ret.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type_with_ret.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0092_fn_pointer_type_with_ret.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type_with_ret.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0092_fn_pointer_type_with_ret.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type_with_ret.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0092_fn_pointer_type_with_ret.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_type_with_ret.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_unnamed_arg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_unnamed_arg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_unnamed_arg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/fn_pointer_unnamed_arg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0100_for_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0100_for_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0100_for_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0100_for_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0142_for_range_from.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_range_from.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0142_for_range_from.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_range_from.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0142_for_range_from.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_range_from.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0142_for_range_from.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_range_from.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0081_for_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0081_for_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0081_for_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0081_for_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0038_full_range_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/full_range_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0038_full_range_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/full_range_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0038_full_range_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/full_range_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0038_full_range_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/full_range_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0086_function_ret_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_ret_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0086_function_ret_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_ret_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0086_function_ret_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_ret_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0086_function_ret_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_ret_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0005_function_type_params.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_type_params.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0005_function_type_params.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_type_params.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0005_function_type_params.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_type_params.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0005_function_type_params.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_type_params.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0076_function_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_where_clause.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0076_function_where_clause.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_where_clause.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0076_function_where_clause.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_where_clause.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0076_function_where_clause.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/function_where_clause.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/gen_blocks.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/gen_blocks.rast
new file mode 100644
index 0000000000000..6e8df9897e13e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/gen_blocks.rast
@@ -0,0 +1,101 @@
+SOURCE_FILE
+  FN
+    COMMENT "// 2024"
+    WHITESPACE "\n"
+    VISIBILITY
+      PUB_KW "pub"
+    WHITESPACE " "
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "main"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          BLOCK_EXPR
+            GEN_KW "gen"
+            WHITESPACE " "
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              EXPR_STMT
+                YIELD_EXPR
+                  YIELD_KW "yield"
+                  WHITESPACE " "
+                  LITERAL
+                    STRING "\"\""
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          BLOCK_EXPR
+            ASYNC_KW "async"
+            WHITESPACE " "
+            GEN_KW "gen"
+            WHITESPACE " "
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              EXPR_STMT
+                YIELD_EXPR
+                  YIELD_KW "yield"
+                  WHITESPACE " "
+                  LITERAL
+                    STRING "\"\""
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          BLOCK_EXPR
+            GEN_KW "gen"
+            WHITESPACE " "
+            MOVE_KW "move"
+            WHITESPACE " "
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              EXPR_STMT
+                YIELD_EXPR
+                  YIELD_KW "yield"
+                  WHITESPACE " "
+                  LITERAL
+                    STRING "\"\""
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          BLOCK_EXPR
+            ASYNC_KW "async"
+            WHITESPACE " "
+            GEN_KW "gen"
+            WHITESPACE " "
+            MOVE_KW "move"
+            WHITESPACE " "
+            STMT_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              EXPR_STMT
+                YIELD_EXPR
+                  YIELD_KW "yield"
+                  WHITESPACE " "
+                  LITERAL
+                    STRING "\"\""
+                SEMICOLON ";"
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/gen_blocks.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/gen_blocks.rs
new file mode 100644
index 0000000000000..669b434973cc4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/gen_blocks.rs
@@ -0,0 +1,7 @@
+// 2024
+pub fn main() {
+    gen { yield ""; };
+    async gen { yield ""; };
+    gen move { yield ""; };
+    async gen move { yield ""; };
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0190_generic_arg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0190_generic_arg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0190_generic_arg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0190_generic_arg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_arg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_generic_param_attribute.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_generic_param_attribute.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_generic_param_attribute.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_generic_param_attribute.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_type_param.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_type_param.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_type_param.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0183_type_param.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/generic_param_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/half_open_range_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/half_open_range_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/half_open_range_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/half_open_range_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0064_if_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/if_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0064_if_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/if_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0064_if_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/if_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0064_if_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/if_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0079_impl_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0079_impl_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0079_impl_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0079_impl_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_impl_item_const.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_const.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_impl_item_const.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_const.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_impl_item_const.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_const.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_impl_item_const.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_const.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0063_impl_item_neg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_neg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0063_impl_item_neg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_neg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0063_impl_item_neg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_neg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0063_impl_item_neg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_item_neg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0028_impl_trait_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_trait_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0028_impl_trait_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_trait_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0028_impl_trait_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_trait_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0028_impl_trait_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_trait_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_impl_type_params.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_type_params.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_impl_type_params.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_type_params.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_impl_type_params.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_type_params.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0150_impl_type_params.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_type_params.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0093_index_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/index_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0093_index_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/index_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0093_index_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/index_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0093_index_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/index_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0109_label.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/label.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0109_label.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/label.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0109_label.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/label.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0109_label.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/label.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_labeled_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/labeled_block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_labeled_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/labeled_block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_labeled_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/labeled_block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0161_labeled_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/labeled_block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_lambda_ret_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_ret_block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_lambda_ret_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_ret_block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_lambda_ret_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_ret_block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_lambda_ret_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_ret_block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_else.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_else.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_else.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_else.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_else.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_else.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_else.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_else.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_let_stmt.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_let_stmt.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_let_stmt.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_let_stmt.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_stmt_ascription.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_ascription.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_stmt_ascription.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_ascription.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_stmt_ascription.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_ascription.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_let_stmt_ascription.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_ascription.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0193_let_stmt_init.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_init.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0193_let_stmt_init.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_init.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0193_let_stmt_init.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_init.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0193_let_stmt_init.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/let_stmt_init.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0186_lifetime_arg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_arg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0186_lifetime_arg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_arg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0186_lifetime_arg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_arg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0186_lifetime_arg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_arg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0182_lifetime_param.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0182_lifetime_param.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0182_lifetime_param.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_param.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0182_lifetime_param.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lifetime_param.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0055_literal_pattern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/literal_pattern.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0055_literal_pattern.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/literal_pattern.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0055_literal_pattern.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/literal_pattern.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0055_literal_pattern.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/literal_pattern.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0009_loop_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/loop_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0009_loop_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/loop_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0009_loop_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/loop_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0009_loop_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/loop_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0117_macro_call_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_call_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0117_macro_call_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_call_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0117_macro_call_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_call_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0117_macro_call_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_call_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_macro_def.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_macro_def.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_macro_def.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0147_macro_def.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_macro_def_curly.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def_curly.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_macro_def_curly.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def_curly.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_macro_def_curly.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def_curly.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_macro_def_curly.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_def_curly.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_macro_inside_generic_arg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_inside_generic_arg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_macro_inside_generic_arg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_inside_generic_arg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_macro_inside_generic_arg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_inside_generic_arg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0194_macro_inside_generic_arg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_inside_generic_arg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_macro_rules_as_macro_name.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_as_macro_name.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_macro_rules_as_macro_name.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_as_macro_name.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_macro_rules_as_macro_name.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_as_macro_name.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_macro_rules_as_macro_name.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_as_macro_name.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_non_brace.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_macro_rules_non_brace.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_non_brace.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_non_brace.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0158_macro_rules_non_brace.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/macro_rules_non_brace.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0129_marco_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/marco_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0129_marco_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/marco_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0129_marco_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/marco_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0129_marco_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/marco_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0066_match_arm.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arm.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0066_match_arm.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arm.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0066_match_arm.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arm.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0066_match_arm.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arm.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0059_match_arms_commas.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_commas.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0059_match_arms_commas.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_commas.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0059_match_arms_commas.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_commas.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0059_match_arms_commas.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_commas.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0071_match_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0071_match_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0071_match_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0071_match_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0118_match_guard.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_guard.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0118_match_guard.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_guard.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0118_match_guard.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_guard.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0118_match_guard.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/match_guard.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/metas.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/metas.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/metas.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/metas.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/method_call_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/method_call_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/method_call_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0107_method_call_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/method_call_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0062_mod_contents.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_contents.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0062_mod_contents.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_contents.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0062_mod_contents.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_contents.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0062_mod_contents.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_contents.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0169_mod_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0169_mod_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0169_mod_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0169_mod_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_mod_item_curly.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item_curly.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_mod_item_curly.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item_curly.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_mod_item_curly.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item_curly.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_mod_item_curly.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/mod_item_curly.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0014_never_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/never_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0014_never_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/never_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0014_never_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/never_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0014_never_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/never_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_semi_after_block.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_semi_after_block.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_semi_after_block.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_semi_after_block.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0113_nocontentexpr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0113_nocontentexpr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0113_nocontentexpr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0113_nocontentexpr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0134_nocontentexpr_after_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr_after_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0134_nocontentexpr_after_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr_after_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0134_nocontentexpr_after_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr_after_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0134_nocontentexpr_after_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/nocontentexpr_after_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_or_pattern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/or_pattern.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_or_pattern.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/or_pattern.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_or_pattern.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/or_pattern.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_or_pattern.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/or_pattern.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0099_param_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0099_param_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0099_param_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0099_param_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0045_param_list_opt_patterns.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0045_param_list_opt_patterns.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0045_param_list_opt_patterns.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0123_param_list_vararg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_vararg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0123_param_list_vararg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_vararg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0123_param_list_vararg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_vararg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0123_param_list_vararg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_list_vararg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0139_param_outer_arg.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0139_param_outer_arg.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0139_param_outer_arg.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_outer_arg.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0139_param_outer_arg.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/param_outer_arg.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0084_paren_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/paren_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0084_paren_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/paren_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0084_paren_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/paren_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0084_paren_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/paren_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0053_path_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0053_path_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0053_path_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0053_path_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0104_path_fn_trait_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0104_path_fn_trait_args.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0104_path_fn_trait_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0104_path_fn_trait_args.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0008_path_part.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_part.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0008_path_part.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_part.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0008_path_part.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_part.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0008_path_part.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_part.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0052_path_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0052_path_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0052_path_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0052_path_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0048_path_type_with_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type_with_bounds.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0048_path_type_with_bounds.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type_with_bounds.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0048_path_type_with_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type_with_bounds.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0048_path_type_with_bounds.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/path_type_with_bounds.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0095_placeholder_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0095_placeholder_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0095_placeholder_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0095_placeholder_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0023_placeholder_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0023_placeholder_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0023_placeholder_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0023_placeholder_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/placeholder_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0013_pointer_type_mut.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pointer_type_mut.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0013_pointer_type_mut.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pointer_type_mut.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0013_pointer_type_mut.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pointer_type_mut.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0013_pointer_type_mut.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pointer_type_mut.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0080_postfix_range.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/postfix_range.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0080_postfix_range.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/postfix_range.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0080_postfix_range.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/postfix_range.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0080_postfix_range.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/postfix_range.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/precise_capturing.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
new file mode 100644
index 0000000000000..cf52f1e479951
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
@@ -0,0 +1,69 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "captures"
+    GENERIC_PARAM_LIST
+      L_ANGLE "<"
+      LIFETIME_PARAM
+        LIFETIME
+          LIFETIME_IDENT "'a"
+        COLON ":"
+        WHITESPACE " "
+        LIFETIME
+          LIFETIME_IDENT "'a"
+      COMMA ","
+      WHITESPACE " "
+      LIFETIME_PARAM
+        LIFETIME
+          LIFETIME_IDENT "'b"
+        COLON ":"
+        WHITESPACE " "
+        LIFETIME
+          LIFETIME_IDENT "'b"
+      COMMA ","
+      WHITESPACE " "
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+      R_ANGLE ">"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    RET_TYPE
+      THIN_ARROW "->"
+      WHITESPACE " "
+      IMPL_TRAIT_TYPE
+        IMPL_KW "impl"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Sized"
+          WHITESPACE " "
+          PLUS "+"
+          WHITESPACE " "
+          TYPE_BOUND
+            USE_KW "use"
+            GENERIC_PARAM_LIST
+              L_ANGLE "<"
+              LIFETIME_PARAM
+                LIFETIME
+                  LIFETIME_IDENT "'b"
+              COMMA ","
+              WHITESPACE " "
+              TYPE_PARAM
+                NAME
+                  IDENT "T"
+              R_ANGLE ">"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/precise_capturing.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/precise_capturing.rs
new file mode 100644
index 0000000000000..ec208d5062b5e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/precise_capturing.rs
@@ -0,0 +1 @@
+fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0153_pub_parens_typepath.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_parens_typepath.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0153_pub_parens_typepath.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_parens_typepath.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0153_pub_parens_typepath.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_parens_typepath.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0153_pub_parens_typepath.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_parens_typepath.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_tuple_field.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_tuple_field.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_tuple_field.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pub_tuple_field.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0037_qual_paths.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0037_qual_paths.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0037_qual_paths.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0037_qual_paths.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/qual_paths.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/range_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0054_record_field_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0054_record_field_attrs.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0054_record_field_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_attrs.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0054_record_field_attrs.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_attrs.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_record_field_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_record_field_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_record_field_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0172_record_field_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0061_record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0061_record_lit.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0061_record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0061_record_lit.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0145_record_pat_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0145_record_pat_field.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0145_record_pat_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0145_record_pat_field.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0102_record_pat_field_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0102_record_pat_field_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0102_record_pat_field_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0102_record_pat_field_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0082_ref_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0082_ref_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0082_ref_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0082_ref_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0027_ref_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0027_ref_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0027_ref_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0027_ref_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0033_reference_type;.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/reference_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0033_reference_type;.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/reference_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0033_reference_type;.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/reference_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0033_reference_type;.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/reference_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0072_return_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0072_return_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0072_return_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0072_return_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0006_self_param.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0006_self_param.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0006_self_param.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0006_self_param.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_self_param_outer_attr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0138_self_param_outer_attr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0046_singleton_tuple_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/singleton_tuple_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0046_singleton_tuple_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/singleton_tuple_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0046_singleton_tuple_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/singleton_tuple_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0046_singleton_tuple_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/singleton_tuple_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0025_slice_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0025_slice_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0025_slice_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0025_slice_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/slice_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0070_stmt_bin_expr_ambiguity.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_bin_expr_ambiguity.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0070_stmt_bin_expr_ambiguity.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_bin_expr_ambiguity.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0070_stmt_bin_expr_ambiguity.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_bin_expr_ambiguity.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0070_stmt_bin_expr_ambiguity.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_bin_expr_ambiguity.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0074_stmt_postfix_expr_ambiguity.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0074_stmt_postfix_expr_ambiguity.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0074_stmt_postfix_expr_ambiguity.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0074_stmt_postfix_expr_ambiguity.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0171_struct_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0171_struct_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0171_struct_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0171_struct_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0041_trait_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0041_trait_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0041_trait_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0041_trait_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0175_trait_item_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_bounds.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0175_trait_item_bounds.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_bounds.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0175_trait_item_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_bounds.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0175_trait_item_bounds.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_bounds.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_trait_item_generic_params.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_generic_params.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_trait_item_generic_params.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_generic_params.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_trait_item_generic_params.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_generic_params.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_trait_item_generic_params.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_generic_params.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_trait_item_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_where_clause.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_trait_item_where_clause.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_where_clause.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_trait_item_where_clause.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_where_clause.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_trait_item_where_clause.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_item_where_clause.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_try_block_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_try_block_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_try_block_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0130_try_block_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0077_try_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0077_try_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0077_try_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0077_try_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_try_macro_fallback.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_fallback.rast
similarity index 94%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_try_macro_fallback.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_fallback.rast
index 0adb678fa65d8..38e21b845dc10 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_try_macro_fallback.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_fallback.rast
@@ -1,5 +1,7 @@
 SOURCE_FILE
   FN
+    COMMENT "// 2015"
+    WHITESPACE "\n"
     FN_KW "fn"
     WHITESPACE " "
     NAME
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_try_macro_fallback.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_fallback.rs
similarity index 77%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_try_macro_fallback.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_fallback.rs
index 61a6b46a0b342..6ad5ea4357c5f 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_try_macro_fallback.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_fallback.rs
@@ -1 +1,2 @@
+// 2015
 fn foo() { try!(Ok(())); }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_try_macro_rules.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_rules.rast
similarity index 91%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_try_macro_rules.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_rules.rast
index e6916ae976ea4..e95fe7625847c 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_try_macro_rules.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_rules.rast
@@ -1,5 +1,7 @@
 SOURCE_FILE
   MACRO_RULES
+    COMMENT "// 2015"
+    WHITESPACE "\n"
     MACRO_RULES_KW "macro_rules"
     BANG "!"
     WHITESPACE " "
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_try_macro_rules.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_rules.rs
similarity index 78%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_try_macro_rules.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_rules.rs
index 2e2ab6e60b641..35694649ece50 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0160_try_macro_rules.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_macro_rules.rs
@@ -1 +1,2 @@
+// 2015
 macro_rules! try { () => {} }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_tuple_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_tuple_attrs.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_tuple_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_attrs.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0154_tuple_attrs.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_attrs.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0108_tuple_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0108_tuple_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0108_tuple_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0108_tuple_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0115_tuple_field_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0115_tuple_field_attrs.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0115_tuple_field_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0115_tuple_field_attrs.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_tuple_struct.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_tuple_struct.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_tuple_struct.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0170_tuple_struct.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0114_tuple_struct_where.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct_where.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0114_tuple_struct_where.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct_where.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0114_tuple_struct_where.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct_where.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0114_tuple_struct_where.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/tuple_struct_where.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0078_type_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_alias.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0078_type_alias.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_alias.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0078_type_alias.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_alias.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0078_type_alias.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_alias.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0073_type_item_type_params.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_type_params.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0073_type_item_type_params.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_type_params.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0073_type_item_type_params.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_type_params.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0073_type_item_type_params.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_type_params.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0012_type_item_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0012_type_item_where_clause.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0012_type_item_where_clause.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0012_type_item_where_clause.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_type_item_where_clause_deprecated.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause_deprecated.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_type_item_where_clause_deprecated.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause_deprecated.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_type_item_where_clause_deprecated.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause_deprecated.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_type_item_where_clause_deprecated.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_item_where_clause_deprecated.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_generic_param_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_generic_param_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_generic_param_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0184_generic_param_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0007_type_param_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0007_type_param_bounds.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0007_type_param_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0007_type_param_bounds.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_bounds.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0090_type_param_default.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_default.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0090_type_param_default.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_default.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0090_type_param_default.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_default.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0090_type_param_default.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_param_default.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_type_path_in_pattern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_type_path_in_pattern.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/type_path_in_pattern.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0019_unary_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unary_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0019_unary_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unary_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0019_unary_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unary_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0019_unary_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unary_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_union_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/union_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_union_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/union_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_union_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/union_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0173_union_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/union_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_unit_struct.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_struct.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_unit_struct.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_struct.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_unit_struct.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_struct.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_unit_struct.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_struct.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0051_unit_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_type.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0051_unit_type.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_type.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0051_unit_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_type.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0051_unit_type.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/unit_type.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_use_item.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_item.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_use_item.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_item.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_use_item.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_item.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0181_use_item.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_item.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0179_use_tree_abs_star.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_abs_star.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0179_use_tree_abs_star.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_abs_star.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0179_use_tree_abs_star.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_abs_star.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0179_use_tree_abs_star.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_abs_star.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_use_tree_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_alias.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_use_tree_alias.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_alias.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_use_tree_alias.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_alias.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0176_use_tree_alias.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_alias.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0002_use_tree_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_list.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0002_use_tree_list.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_list.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0002_use_tree_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_list.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0002_use_tree_list.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_list.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree_path.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_use_tree_path.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0180_use_tree_path_star.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_star.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0180_use_tree_path_star.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_star.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0180_use_tree_path_star.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_star.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0180_use_tree_path_star.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_star.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_use_tree.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_use_tree.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_use_tree.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0178_use_tree_path_use_tree.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_path_use_tree.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_use_tree_star.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_star.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_use_tree_star.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_star.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_use_tree_star.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_star.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0174_use_tree_star.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/use_tree_star.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0004_value_parameters_no_patterns.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0004_value_parameters_no_patterns.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0004_value_parameters_no_patterns.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0004_value_parameters_no_patterns.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_variant_discriminant.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/variant_discriminant.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_variant_discriminant.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/variant_discriminant.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_variant_discriminant.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/variant_discriminant.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0157_variant_discriminant.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/variant_discriminant.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0056_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0056_where_clause.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0056_where_clause.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0056_where_clause.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_clause.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0003_where_pred_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0003_where_pred_for.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0003_where_pred_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0003_where_pred_for.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0031_while_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/while_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0031_while_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/while_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0031_while_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/while_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0031_while_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/while_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0204_yeet_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yeet_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0204_yeet_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yeet_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0204_yeet_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yeet_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0204_yeet_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yeet_expr.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_yield_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yield_expr.rast
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_yield_expr.rast
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yield_expr.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_yield_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yield_expr.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0159_yield_expr.rs
rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/yield_expr.rs
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
index 7f633d91ecc8c..345fb9f8ae973 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
@@ -28,6 +28,7 @@ span.workspace = true
 # InternIds for the syntax context
 base-db.workspace = true
 la-arena.workspace = true
+intern.workspace = true
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 3a915e668bbff..54c1475b8b1c8 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -13,7 +13,6 @@ use base_db::Env;
 use paths::{AbsPath, AbsPathBuf};
 use span::Span;
 use std::{fmt, io, sync::Arc};
-use tt::SmolStr;
 
 use serde::{Deserialize, Serialize};
 
@@ -66,7 +65,7 @@ impl MacroDylib {
 pub struct ProcMacro {
     process: Arc,
     dylib_path: Arc,
-    name: SmolStr,
+    name: Box,
     kind: ProcMacroKind,
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
index fa3ba9bbfcd73..6a99b5ed1cc84 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
@@ -19,8 +19,10 @@ pub const VERSION_CHECK_VERSION: u32 = 1;
 pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
 pub const HAS_GLOBAL_SPANS: u32 = 3;
 pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
+/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field
+pub const EXTENDED_LEAF_DATA: u32 = 5;
 
-pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT;
+pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA;
 
 #[derive(Debug, Serialize, Deserialize)]
 pub enum Request {
@@ -155,7 +157,7 @@ type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str)
 
 #[cfg(test)]
 mod tests {
-    use base_db::FileId;
+    use intern::{sym, Symbol};
     use la_arena::RawIdx;
     use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId};
     use text_size::{TextRange, TextSize};
@@ -165,46 +167,52 @@ mod tests {
 
     fn fixture_token_tree() -> Subtree {
         let anchor = SpanAnchor {
-            file_id: FileId::from_raw(0),
+            file_id: span::EditionedFileId::new(
+                span::FileId::from_raw(0xe4e4e),
+                span::Edition::CURRENT,
+            ),
             ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)),
         };
 
         let token_trees = Box::new([
             TokenTree::Leaf(
                 Ident {
-                    text: "struct".into(),
+                    sym: Symbol::intern("struct"),
                     span: Span {
                         range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
                         anchor,
                         ctx: SyntaxContextId::ROOT,
                     },
+                    is_raw: tt::IdentIsRaw::No,
                 }
                 .into(),
             ),
             TokenTree::Leaf(
                 Ident {
-                    text: "Foo".into(),
+                    sym: Symbol::intern("Foo"),
                     span: Span {
-                        range: TextRange::at(TextSize::new(5), TextSize::of("Foo")),
+                        range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
                         anchor,
                         ctx: SyntaxContextId::ROOT,
                     },
+                    is_raw: tt::IdentIsRaw::Yes,
                 }
                 .into(),
             ),
             TokenTree::Leaf(Leaf::Literal(Literal {
-                text: "Foo".into(),
-
+                symbol: Symbol::intern("Foo"),
                 span: Span {
-                    range: TextRange::at(TextSize::new(8), TextSize::of("Foo")),
+                    range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
                 },
+                kind: tt::LitKind::Str,
+                suffix: None,
             })),
             TokenTree::Leaf(Leaf::Punct(Punct {
                 char: '@',
                 span: Span {
-                    range: TextRange::at(TextSize::new(11), TextSize::of('@')),
+                    range: TextRange::at(TextSize::new(13), TextSize::of('@')),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
                 },
@@ -213,18 +221,27 @@ mod tests {
             TokenTree::Subtree(Subtree {
                 delimiter: Delimiter {
                     open: Span {
-                        range: TextRange::at(TextSize::new(12), TextSize::of('{')),
+                        range: TextRange::at(TextSize::new(14), TextSize::of('{')),
                         anchor,
                         ctx: SyntaxContextId::ROOT,
                     },
                     close: Span {
-                        range: TextRange::at(TextSize::new(13), TextSize::of('}')),
+                        range: TextRange::at(TextSize::new(19), TextSize::of('}')),
                         anchor,
                         ctx: SyntaxContextId::ROOT,
                     },
                     kind: DelimiterKind::Brace,
                 },
-                token_trees: Box::new([]),
+                token_trees: Box::new([TokenTree::Leaf(Leaf::Literal(Literal {
+                    symbol: sym::INTEGER_0.clone(),
+                    span: Span {
+                        range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
+                        anchor,
+                        ctx: SyntaxContextId::ROOT,
+                    },
+                    kind: tt::LitKind::Integer,
+                    suffix: Some(sym::u32.clone()),
+                }))]),
             }),
         ]);
 
@@ -236,7 +253,7 @@ mod tests {
                     ctx: SyntaxContextId::ROOT,
                 },
                 close: Span {
-                    range: TextRange::empty(TextSize::new(13)),
+                    range: TextRange::empty(TextSize::new(19)),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
                 },
@@ -249,32 +266,35 @@ mod tests {
     #[test]
     fn test_proc_macro_rpc_works() {
         let tt = fixture_token_tree();
-        let mut span_data_table = Default::default();
-        let task = ExpandMacro {
-            data: ExpandMacroData {
-                macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table),
-                macro_name: Default::default(),
-                attributes: None,
-                has_global_spans: ExpnGlobals {
-                    serialize: true,
-                    def_site: 0,
-                    call_site: 0,
-                    mixed_site: 0,
+        for v in RUST_ANALYZER_SPAN_SUPPORT..=CURRENT_API_VERSION {
+            let mut span_data_table = Default::default();
+            let task = ExpandMacro {
+                data: ExpandMacroData {
+                    macro_body: FlatTree::new(&tt, v, &mut span_data_table),
+                    macro_name: Default::default(),
+                    attributes: None,
+                    has_global_spans: ExpnGlobals {
+                        serialize: true,
+                        def_site: 0,
+                        call_site: 0,
+                        mixed_site: 0,
+                    },
+                    span_data_table: Vec::new(),
                 },
-                span_data_table: Vec::new(),
-            },
-            lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
-            env: Default::default(),
-            current_dir: Default::default(),
-        };
+                lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
+                env: Default::default(),
+                current_dir: Default::default(),
+            };
 
-        let json = serde_json::to_string(&task).unwrap();
-        // println!("{}", json);
-        let back: ExpandMacro = serde_json::from_str(&json).unwrap();
+            let json = serde_json::to_string(&task).unwrap();
+            // println!("{}", json);
+            let back: ExpandMacro = serde_json::from_str(&json).unwrap();
 
-        assert_eq!(
-            tt,
-            back.data.macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table)
-        );
+            assert_eq!(
+                tt,
+                back.data.macro_body.to_subtree_resolved(v, &span_data_table),
+                "version: {v}"
+            );
+        }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
index 11fd7596f2b52..a8661f59b2887 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
@@ -37,13 +37,14 @@
 
 use std::collections::VecDeque;
 
+use intern::Symbol;
 use la_arena::RawIdx;
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
-use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId};
+use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId};
 use text_size::TextRange;
 
-use crate::msg::ENCODE_CLOSE_SPAN_VERSION;
+use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA};
 
 pub type SpanDataIndexMap =
     indexmap::IndexSet>;
@@ -52,7 +53,7 @@ pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec {
     map.iter()
         .flat_map(|span| {
             [
-                span.anchor.file_id.index(),
+                span.anchor.file_id.as_u32(),
                 span.anchor.ast_id.into_raw().into_u32(),
                 span.range.start().into(),
                 span.range.end().into(),
@@ -69,7 +70,7 @@ pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap {
             let &[file_id, ast_id, start, end, e] = span else { unreachable!() };
             Span {
                 anchor: SpanAnchor {
-                    file_id: FileId::from_raw(file_id),
+                    file_id: EditionedFileId::from_raw(file_id),
                     ast_id: ErasedFileAstId::from_raw(RawIdx::from_u32(ast_id)),
                 },
                 range: TextRange::new(start.into(), end.into()),
@@ -108,6 +109,8 @@ struct SubtreeRepr {
 struct LiteralRepr {
     id: TokenId,
     text: u32,
+    suffix: u32,
+    kind: u16,
 }
 
 struct PunctRepr {
@@ -119,6 +122,7 @@ struct PunctRepr {
 struct IdentRepr {
     id: TokenId,
     text: u32,
+    is_raw: bool,
 }
 
 impl FlatTree {
@@ -138,6 +142,7 @@ impl FlatTree {
             ident: Vec::new(),
             token_tree: Vec::new(),
             text: Vec::new(),
+            version,
         };
         w.write(subtree);
 
@@ -147,9 +152,17 @@ impl FlatTree {
             } else {
                 write_vec(w.subtree, SubtreeRepr::write)
             },
-            literal: write_vec(w.literal, LiteralRepr::write),
+            literal: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.literal, LiteralRepr::write_with_kind)
+            } else {
+                write_vec(w.literal, LiteralRepr::write)
+            },
             punct: write_vec(w.punct, PunctRepr::write),
-            ident: write_vec(w.ident, IdentRepr::write),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.ident, IdentRepr::write_with_rawness)
+            } else {
+                write_vec(w.ident, IdentRepr::write)
+            },
             token_tree: w.token_tree,
             text: w.text,
         }
@@ -167,6 +180,7 @@ impl FlatTree {
             ident: Vec::new(),
             token_tree: Vec::new(),
             text: Vec::new(),
+            version,
         };
         w.write(subtree);
 
@@ -176,9 +190,17 @@ impl FlatTree {
             } else {
                 write_vec(w.subtree, SubtreeRepr::write)
             },
-            literal: write_vec(w.literal, LiteralRepr::write),
+            literal: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.literal, LiteralRepr::write_with_kind)
+            } else {
+                write_vec(w.literal, LiteralRepr::write)
+            },
             punct: write_vec(w.punct, PunctRepr::write),
-            ident: write_vec(w.ident, IdentRepr::write),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.ident, IdentRepr::write_with_rawness)
+            } else {
+                write_vec(w.ident, IdentRepr::write)
+            },
             token_tree: w.token_tree,
             text: w.text,
         }
@@ -195,12 +217,21 @@ impl FlatTree {
             } else {
                 read_vec(self.subtree, SubtreeRepr::read)
             },
-            literal: read_vec(self.literal, LiteralRepr::read),
+            literal: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.literal, LiteralRepr::read_with_kind)
+            } else {
+                read_vec(self.literal, LiteralRepr::read)
+            },
             punct: read_vec(self.punct, PunctRepr::read),
-            ident: read_vec(self.ident, IdentRepr::read),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.ident, IdentRepr::read_with_rawness)
+            } else {
+                read_vec(self.ident, IdentRepr::read)
+            },
             token_tree: self.token_tree,
             text: self.text,
             span_data_table,
+            version,
         }
         .read()
     }
@@ -212,12 +243,21 @@ impl FlatTree {
             } else {
                 read_vec(self.subtree, SubtreeRepr::read)
             },
-            literal: read_vec(self.literal, LiteralRepr::read),
+            literal: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.literal, LiteralRepr::read_with_kind)
+            } else {
+                read_vec(self.literal, LiteralRepr::read)
+            },
             punct: read_vec(self.punct, PunctRepr::read),
-            ident: read_vec(self.ident, IdentRepr::read),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.ident, IdentRepr::read_with_rawness)
+            } else {
+                read_vec(self.ident, IdentRepr::read)
+            },
             token_tree: self.token_tree,
             text: self.text,
             span_data_table: &(),
+            version,
         }
         .read()
     }
@@ -280,14 +320,20 @@ impl LiteralRepr {
         [self.id.0, self.text]
     }
     fn read([id, text]: [u32; 2]) -> LiteralRepr {
-        LiteralRepr { id: TokenId(id), text }
+        LiteralRepr { id: TokenId(id), text, kind: 0, suffix: !0 }
+    }
+    fn write_with_kind(self) -> [u32; 4] {
+        [self.id.0, self.text, self.kind as u32, self.suffix]
+    }
+    fn read_with_kind([id, text, kind, suffix]: [u32; 4]) -> LiteralRepr {
+        LiteralRepr { id: TokenId(id), text, kind: kind as u16, suffix }
     }
 }
 
 impl PunctRepr {
     fn write(self) -> [u32; 3] {
         let spacing = match self.spacing {
-            tt::Spacing::Alone => 0,
+            tt::Spacing::Alone | tt::Spacing::JointHidden => 0,
             tt::Spacing::Joint => 1,
         };
         [self.id.0, self.char as u32, spacing]
@@ -307,7 +353,13 @@ impl IdentRepr {
         [self.id.0, self.text]
     }
     fn read(data: [u32; 2]) -> IdentRepr {
-        IdentRepr { id: TokenId(data[0]), text: data[1] }
+        IdentRepr { id: TokenId(data[0]), text: data[1], is_raw: false }
+    }
+    fn write_with_rawness(self) -> [u32; 3] {
+        [self.id.0, self.text, self.is_raw as u32]
+    }
+    fn read_with_rawness([id, text, is_raw]: [u32; 3]) -> IdentRepr {
+        IdentRepr { id: TokenId(id), text, is_raw: is_raw == 1 }
     }
 }
 
@@ -339,8 +391,9 @@ impl InternableSpan for Span {
 
 struct Writer<'a, 'span, S: InternableSpan> {
     work: VecDeque<(usize, &'a tt::Subtree)>,
-    string_table: FxHashMap<&'a str, u32>,
+    string_table: FxHashMap, u32>,
     span_data_table: &'span mut S::Table,
+    version: u32,
 
     subtree: Vec,
     literal: Vec,
@@ -378,9 +431,33 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> {
                 tt::TokenTree::Leaf(leaf) => match leaf {
                     tt::Leaf::Literal(lit) => {
                         let idx = self.literal.len() as u32;
-                        let text = self.intern(&lit.text);
                         let id = self.token_id_of(lit.span);
-                        self.literal.push(LiteralRepr { id, text });
+                        let (text, suffix) = if self.version >= EXTENDED_LEAF_DATA {
+                            (
+                                self.intern(lit.symbol.as_str()),
+                                lit.suffix.as_ref().map(|s| self.intern(s.as_str())).unwrap_or(!0),
+                            )
+                        } else {
+                            (self.intern_owned(format!("{lit}")), !0)
+                        };
+                        self.literal.push(LiteralRepr {
+                            id,
+                            text,
+                            kind: u16::from_le_bytes(match lit.kind {
+                                tt::LitKind::Err(_) => [0, 0],
+                                tt::LitKind::Byte => [1, 0],
+                                tt::LitKind::Char => [2, 0],
+                                tt::LitKind::Integer => [3, 0],
+                                tt::LitKind::Float => [4, 0],
+                                tt::LitKind::Str => [5, 0],
+                                tt::LitKind::StrRaw(r) => [6, r],
+                                tt::LitKind::ByteStr => [7, 0],
+                                tt::LitKind::ByteStrRaw(r) => [8, r],
+                                tt::LitKind::CStr => [9, 0],
+                                tt::LitKind::CStrRaw(r) => [10, r],
+                            }),
+                            suffix,
+                        });
                         idx << 2 | 0b01
                     }
                     tt::Leaf::Punct(punct) => {
@@ -391,9 +468,15 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> {
                     }
                     tt::Leaf::Ident(ident) => {
                         let idx = self.ident.len() as u32;
-                        let text = self.intern(&ident.text);
                         let id = self.token_id_of(ident.span);
-                        self.ident.push(IdentRepr { id, text });
+                        let text = if self.version >= EXTENDED_LEAF_DATA {
+                            self.intern(ident.sym.as_str())
+                        } else if ident.is_raw.yes() {
+                            self.intern_owned(format!("r#{}", ident.sym.as_str(),))
+                        } else {
+                            self.intern(ident.sym.as_str())
+                        };
+                        self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() });
                         idx << 2 | 0b11
                     }
                 },
@@ -415,15 +498,25 @@ impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> {
 
     pub(crate) fn intern(&mut self, text: &'a str) -> u32 {
         let table = &mut self.text;
-        *self.string_table.entry(text).or_insert_with(|| {
+        *self.string_table.entry(text.into()).or_insert_with(|| {
             let idx = table.len();
             table.push(text.to_owned());
             idx as u32
         })
     }
+
+    pub(crate) fn intern_owned(&mut self, text: String) -> u32 {
+        let table = &mut self.text;
+        *self.string_table.entry(text.clone().into()).or_insert_with(|| {
+            let idx = table.len();
+            table.push(text);
+            idx as u32
+        })
+    }
 }
 
 struct Reader<'span, S: InternableSpan> {
+    version: u32,
     subtree: Vec,
     literal: Vec,
     punct: Vec,
@@ -457,10 +550,38 @@ impl<'span, S: InternableSpan> Reader<'span, S> {
                             // that this unwrap doesn't fire.
                             0b00 => res[idx].take().unwrap().into(),
                             0b01 => {
+                                use tt::LitKind::*;
                                 let repr = &self.literal[idx];
-                                tt::Leaf::Literal(tt::Literal {
-                                    text: self.text[repr.text as usize].as_str().into(),
-                                    span: read_span(repr.id),
+                                let text = self.text[repr.text as usize].as_str();
+                                let span = read_span(repr.id);
+                                tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA {
+                                    tt::Literal {
+                                        symbol: Symbol::intern(text),
+                                        span,
+                                        kind: match u16::to_le_bytes(repr.kind) {
+                                            [0, _] => Err(()),
+                                            [1, _] => Byte,
+                                            [2, _] => Char,
+                                            [3, _] => Integer,
+                                            [4, _] => Float,
+                                            [5, _] => Str,
+                                            [6, r] => StrRaw(r),
+                                            [7, _] => ByteStr,
+                                            [8, r] => ByteStrRaw(r),
+                                            [9, _] => CStr,
+                                            [10, r] => CStrRaw(r),
+                                            _ => unreachable!(),
+                                        },
+                                        suffix: if repr.suffix != !0 {
+                                            Some(Symbol::intern(
+                                                self.text[repr.suffix as usize].as_str(),
+                                            ))
+                                        } else {
+                                            None
+                                        },
+                                    }
+                                } else {
+                                    tt::token_to_literal(text, span)
                                 })
                                 .into()
                             }
@@ -475,9 +596,23 @@ impl<'span, S: InternableSpan> Reader<'span, S> {
                             }
                             0b11 => {
                                 let repr = &self.ident[idx];
+                                let text = self.text[repr.text as usize].as_str();
+                                let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA {
+                                    (
+                                        if repr.is_raw {
+                                            tt::IdentIsRaw::Yes
+                                        } else {
+                                            tt::IdentIsRaw::No
+                                        },
+                                        text,
+                                    )
+                                } else {
+                                    tt::IdentIsRaw::split_from_symbol(text)
+                                };
                                 tt::Leaf::Ident(tt::Ident {
-                                    text: self.text[repr.text as usize].as_str().into(),
+                                    sym: Symbol::intern(text),
                                     span: read_span(repr.id),
+                                    is_raw,
                                 })
                                 .into()
                             }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
index 735f781c43910..673b5bd78a8e9 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
@@ -25,6 +25,7 @@ base-db.workspace = true
 span.workspace = true
 proc-macro-api.workspace = true
 ra-ap-rustc_lexer.workspace = true
+intern.workspace = true
 
 [dev-dependencies]
 expect-test = "1.4.0"
@@ -34,7 +35,7 @@ proc-macro-test.path = "./proc-macro-test"
 
 [features]
 sysroot-abi = []
-in-rust-tree = ["mbe/in-rust-tree", "sysroot-abi"]
+in-rust-tree = ["mbe/in-rust-tree", "tt/in-rust-tree","sysroot-abi"]
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
index a1707364f3c21..749a7760592b5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
@@ -1,6 +1,5 @@
 //! Exports a few trivial procedural macros for testing.
 
-
 #![feature(proc_macro_span, proc_macro_def_site)]
 #![allow(clippy::all)]
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index e6281035e1a2e..f0aa6b3f93f95 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -130,14 +130,13 @@ impl ProcMacroSrvSpan for TokenId {
     type Server = server_impl::token_id::TokenIdServer;
 
     fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server {
-        Self::Server { interner: &server_impl::SYMBOL_INTERNER, call_site, def_site, mixed_site }
+        Self::Server { call_site, def_site, mixed_site }
     }
 }
 impl ProcMacroSrvSpan for Span {
     type Server = server_impl::rust_analyzer_span::RaSpanServer;
     fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server {
         Self::Server {
-            interner: &server_impl::SYMBOL_INTERNER,
             call_site,
             def_site,
             mixed_site,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
index e8b340a43d305..c9a8621690552 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
@@ -14,9 +14,9 @@ mod token_stream;
 pub use token_stream::TokenStream;
 
 pub mod rust_analyzer_span;
-mod symbol;
+// mod symbol;
 pub mod token_id;
-pub use symbol::*;
+// pub use symbol::*;
 use tt::Spacing;
 
 fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter {
@@ -49,58 +49,39 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
 #[allow(unused)]
 fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
     match spacing {
-        Spacing::Alone => proc_macro::Spacing::Alone,
+        Spacing::Alone | Spacing::JointHidden => proc_macro::Spacing::Alone,
         Spacing::Joint => proc_macro::Spacing::Joint,
     }
 }
 
-/// Invokes the callback with a `&[&str]` consisting of each part of the
-/// literal's representation. This is done to allow the `ToString` and
-/// `Display` implementations to borrow references to symbol values, and
-/// both be optimized to reduce overhead.
-fn literal_with_stringify_parts(
-    literal: &bridge::Literal,
-    interner: SymbolInternerRef,
-    f: impl FnOnce(&[&str]) -> R,
-) -> R {
-    /// Returns a string containing exactly `num` '#' characters.
-    /// Uses a 256-character source string literal which is always safe to
-    /// index with a `u8` index.
-    fn get_hashes_str(num: u8) -> &'static str {
-        const HASHES: &str = "\
-                        ################################################################\
-                        ################################################################\
-                        ################################################################\
-                        ################################################################\
-                        ";
-        const _: () = assert!(HASHES.len() == 256);
-        &HASHES[..num as usize]
+fn literal_kind_to_external(kind: tt::LitKind) -> bridge::LitKind {
+    match kind {
+        tt::LitKind::Byte => bridge::LitKind::Byte,
+        tt::LitKind::Char => bridge::LitKind::Char,
+        tt::LitKind::Integer => bridge::LitKind::Integer,
+        tt::LitKind::Float => bridge::LitKind::Float,
+        tt::LitKind::Str => bridge::LitKind::Str,
+        tt::LitKind::StrRaw(r) => bridge::LitKind::StrRaw(r),
+        tt::LitKind::ByteStr => bridge::LitKind::ByteStr,
+        tt::LitKind::ByteStrRaw(r) => bridge::LitKind::ByteStrRaw(r),
+        tt::LitKind::CStr => bridge::LitKind::CStr,
+        tt::LitKind::CStrRaw(r) => bridge::LitKind::CStrRaw(r),
+        tt::LitKind::Err(_) => bridge::LitKind::ErrWithGuar,
     }
+}
 
-    {
-        let symbol = &*literal.symbol.text(interner);
-        let suffix = &*literal.suffix.map(|s| s.text(interner)).unwrap_or_default();
-        match literal.kind {
-            bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
-            bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
-            bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
-            bridge::LitKind::StrRaw(n) => {
-                let hashes = get_hashes_str(n);
-                f(&["r", hashes, "\"", symbol, "\"", hashes, suffix])
-            }
-            bridge::LitKind::ByteStr => f(&["b\"", symbol, "\"", suffix]),
-            bridge::LitKind::ByteStrRaw(n) => {
-                let hashes = get_hashes_str(n);
-                f(&["br", hashes, "\"", symbol, "\"", hashes, suffix])
-            }
-            bridge::LitKind::CStr => f(&["c\"", symbol, "\"", suffix]),
-            bridge::LitKind::CStrRaw(n) => {
-                let hashes = get_hashes_str(n);
-                f(&["cr", hashes, "\"", symbol, "\"", hashes, suffix])
-            }
-            bridge::LitKind::Integer | bridge::LitKind::Float | bridge::LitKind::ErrWithGuar => {
-                f(&[symbol, suffix])
-            }
-        }
+fn literal_kind_to_internal(kind: bridge::LitKind) -> tt::LitKind {
+    match kind {
+        bridge::LitKind::Byte => tt::LitKind::Byte,
+        bridge::LitKind::Char => tt::LitKind::Char,
+        bridge::LitKind::Str => tt::LitKind::Str,
+        bridge::LitKind::StrRaw(r) => tt::LitKind::StrRaw(r),
+        bridge::LitKind::ByteStr => tt::LitKind::ByteStr,
+        bridge::LitKind::ByteStrRaw(r) => tt::LitKind::ByteStrRaw(r),
+        bridge::LitKind::CStr => tt::LitKind::CStr,
+        bridge::LitKind::CStrRaw(r) => tt::LitKind::CStrRaw(r),
+        bridge::LitKind::Integer => tt::LitKind::Integer,
+        bridge::LitKind::Float => tt::LitKind::Float,
+        bridge::LitKind::ErrWithGuar => tt::LitKind::Err(()),
     }
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index bb174ba1b2243..8b9eb3beb6e88 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -10,13 +10,14 @@ use std::{
     ops::{Bound, Range},
 };
 
+use intern::Symbol;
 use proc_macro::bridge::{self, server};
 use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
 use tt::{TextRange, TextSize};
 
 use crate::server_impl::{
-    delim_to_external, delim_to_internal, literal_with_stringify_parts,
-    token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER,
+    delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal,
+    token_stream::TokenStreamBuilder,
 };
 mod tt {
     pub use tt::*;
@@ -36,7 +37,6 @@ pub struct SourceFile;
 pub struct FreeFunctions;
 
 pub struct RaSpanServer {
-    pub(crate) interner: SymbolInternerRef,
     // FIXME: Report this back to the caller to track as dependencies
     pub tracked_env_vars: HashMap, Option>>,
     // FIXME: Report this back to the caller to track as dependencies
@@ -126,15 +126,10 @@ impl server::FreeFunctions for RaSpanServer {
         let lit = &lit[start_offset..lit.len() - end_offset];
         let suffix = match suffix {
             "" | "_" => None,
-            suffix => Some(Symbol::intern(self.interner, suffix)),
+            suffix => Some(Symbol::intern(suffix)),
         };
 
-        Ok(bridge::Literal {
-            kind,
-            symbol: Symbol::intern(self.interner, lit),
-            suffix,
-            span: self.call_site,
-        })
+        Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span: self.call_site })
     }
 
     fn emit_diagnostic(&mut self, _: bridge::Diagnostic) {
@@ -170,21 +165,25 @@ impl server::TokenStream for RaSpanServer {
             }
 
             bridge::TokenTree::Ident(ident) => {
-                let text = ident.sym.text(self.interner);
-                let text =
-                    if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text };
-                let ident: tt::Ident = tt::Ident { text, span: ident.span };
+                let text = ident.sym;
+                let ident: tt::Ident = tt::Ident {
+                    sym: text,
+                    span: ident.span,
+                    is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
+                };
                 let leaf = tt::Leaf::from(ident);
                 let tree = tt::TokenTree::from(leaf);
                 Self::TokenStream::from_iter(iter::once(tree))
             }
 
             bridge::TokenTree::Literal(literal) => {
-                let text = literal_with_stringify_parts(&literal, self.interner, |parts| {
-                    ::tt::SmolStr::from_iter(parts.iter().copied())
-                });
+                let literal = tt::Literal {
+                    symbol: literal.symbol,
+                    suffix: literal.suffix,
+                    span: literal.span,
+                    kind: literal_kind_to_internal(literal.kind),
+                };
 
-                let literal = tt::Literal { text, span: literal.span };
                 let leaf: tt::Leaf = tt::Leaf::from(literal);
                 let tree = tt::TokenTree::from(leaf);
                 Self::TokenStream::from_iter(iter::once(tree))
@@ -250,23 +249,18 @@ impl server::TokenStream for RaSpanServer {
             .into_iter()
             .map(|tree| match tree {
                 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
-                    bridge::TokenTree::Ident(match ident.text.strip_prefix("r#") {
-                        Some(text) => bridge::Ident {
-                            sym: Symbol::intern(self.interner, text),
-                            is_raw: true,
-                            span: ident.span,
-                        },
-                        None => bridge::Ident {
-                            sym: Symbol::intern(self.interner, &ident.text),
-                            is_raw: false,
-                            span: ident.span,
-                        },
+                    bridge::TokenTree::Ident(bridge::Ident {
+                        sym: ident.sym,
+                        is_raw: ident.is_raw.yes(),
+                        span: ident.span,
                     })
                 }
                 tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
                     bridge::TokenTree::Literal(bridge::Literal {
                         span: lit.span,
-                        ..server::FreeFunctions::literal_from_str(self, &lit.text).unwrap()
+                        kind: literal_kind_to_external(lit.kind),
+                        symbol: lit.symbol,
+                        suffix: lit.suffix,
                     })
                 }
                 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
@@ -465,12 +459,95 @@ impl server::Server for RaSpanServer {
     }
 
     fn intern_symbol(ident: &str) -> Self::Symbol {
-        // FIXME: should be `self.interner` once the proc-macro api allows it.
-        Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident))
+        Symbol::intern(ident)
     }
 
     fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
-        // FIXME: should be `self.interner` once the proc-macro api allows it.
-        f(symbol.text(&SYMBOL_INTERNER).as_str())
+        f(symbol.as_str())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use span::{EditionedFileId, FileId, SyntaxContextId};
+
+    use super::*;
+
+    #[test]
+    fn test_ra_server_to_string() {
+        let span = Span {
+            range: TextRange::empty(TextSize::new(0)),
+            anchor: span::SpanAnchor {
+                file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
+                ast_id: span::ErasedFileAstId::from_raw(0.into()),
+            },
+            ctx: SyntaxContextId::ROOT,
+        };
+        let s = TokenStream {
+            token_trees: vec![
+                tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                    sym: Symbol::intern("struct"),
+                    span,
+                    is_raw: tt::IdentIsRaw::No,
+                })),
+                tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                    sym: Symbol::intern("T"),
+                    span: span,
+                    is_raw: tt::IdentIsRaw::No,
+                })),
+                tt::TokenTree::Subtree(tt::Subtree {
+                    delimiter: tt::Delimiter {
+                        open: span,
+                        close: span,
+                        kind: tt::DelimiterKind::Brace,
+                    },
+                    token_trees: Box::new([]),
+                }),
+            ],
+        };
+
+        assert_eq!(s.to_string(), "struct T {}");
+    }
+
+    #[test]
+    fn test_ra_server_from_str() {
+        let span = Span {
+            range: TextRange::empty(TextSize::new(0)),
+            anchor: span::SpanAnchor {
+                file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
+                ast_id: span::ErasedFileAstId::from_raw(0.into()),
+            },
+            ctx: SyntaxContextId::ROOT,
+        };
+        let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
+            delimiter: tt::Delimiter {
+                open: span,
+                close: span,
+                kind: tt::DelimiterKind::Parenthesis,
+            },
+            token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                is_raw: tt::IdentIsRaw::No,
+                sym: Symbol::intern("a"),
+                span,
+            }))]),
+        });
+
+        let t1 = TokenStream::from_str("(a)", span).unwrap();
+        assert_eq!(t1.token_trees.len(), 1);
+        assert_eq!(t1.token_trees[0], subtree_paren_a);
+
+        let t2 = TokenStream::from_str("(a);", span).unwrap();
+        assert_eq!(t2.token_trees.len(), 2);
+        assert_eq!(t2.token_trees[0], subtree_paren_a);
+
+        let underscore = TokenStream::from_str("_", span).unwrap();
+        assert_eq!(
+            underscore.token_trees[0],
+            tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                sym: Symbol::intern("_"),
+                span,
+                is_raw: tt::IdentIsRaw::No,
+            }))
+        );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs
index 540d06457f2f3..6863ce959973e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/symbol.rs
@@ -1,7 +1,6 @@
 //! Symbol interner for proc-macro-srv
 
 use std::{cell::RefCell, collections::HashMap, thread::LocalKey};
-use tt::SmolStr;
 
 thread_local! {
     pub(crate) static SYMBOL_INTERNER: RefCell = Default::default();
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
index 12edacbe39dc6..7720c6d83c38d 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -5,11 +5,12 @@ use std::{
     ops::{Bound, Range},
 };
 
+use intern::Symbol;
 use proc_macro::bridge::{self, server};
 
 use crate::server_impl::{
-    delim_to_external, delim_to_internal, literal_with_stringify_parts,
-    token_stream::TokenStreamBuilder, Symbol, SymbolInternerRef, SYMBOL_INTERNER,
+    delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal,
+    token_stream::TokenStreamBuilder,
 };
 mod tt {
     pub use proc_macro_api::msg::TokenId;
@@ -25,10 +26,8 @@ mod tt {
 }
 type Group = tt::Subtree;
 type TokenTree = tt::TokenTree;
-#[allow(unused)]
 type Punct = tt::Punct;
 type Spacing = tt::Spacing;
-#[allow(unused)]
 type Literal = tt::Literal;
 type Span = tt::TokenId;
 type TokenStream = crate::server_impl::TokenStream;
@@ -38,7 +37,6 @@ pub struct SourceFile;
 pub struct FreeFunctions;
 
 pub struct TokenIdServer {
-    pub(crate) interner: SymbolInternerRef,
     pub call_site: Span,
     pub def_site: Span,
     pub mixed_site: Span,
@@ -119,15 +117,10 @@ impl server::FreeFunctions for TokenIdServer {
         let lit = &lit[start_offset..lit.len() - end_offset];
         let suffix = match suffix {
             "" | "_" => None,
-            suffix => Some(Symbol::intern(self.interner, suffix)),
+            suffix => Some(Symbol::intern(suffix)),
         };
 
-        Ok(bridge::Literal {
-            kind,
-            symbol: Symbol::intern(self.interner, lit),
-            suffix,
-            span: self.call_site,
-        })
+        Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span: self.call_site })
     }
 
     fn emit_diagnostic(&mut self, _: bridge::Diagnostic) {}
@@ -161,21 +154,23 @@ impl server::TokenStream for TokenIdServer {
             }
 
             bridge::TokenTree::Ident(ident) => {
-                let text = ident.sym.text(self.interner);
-                let text =
-                    if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text };
-                let ident: tt::Ident = tt::Ident { text, span: ident.span };
+                let ident: tt::Ident = tt::Ident {
+                    sym: ident.sym,
+                    span: ident.span,
+                    is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
+                };
                 let leaf = tt::Leaf::from(ident);
                 let tree = TokenTree::from(leaf);
                 Self::TokenStream::from_iter(iter::once(tree))
             }
 
             bridge::TokenTree::Literal(literal) => {
-                let text = literal_with_stringify_parts(&literal, self.interner, |parts| {
-                    ::tt::SmolStr::from_iter(parts.iter().copied())
-                });
-
-                let literal = tt::Literal { text, span: literal.span };
+                let literal = Literal {
+                    symbol: literal.symbol,
+                    suffix: literal.suffix,
+                    span: literal.span,
+                    kind: literal_kind_to_internal(literal.kind),
+                };
 
                 let leaf = tt::Leaf::from(literal);
                 let tree = TokenTree::from(leaf);
@@ -183,7 +178,7 @@ impl server::TokenStream for TokenIdServer {
             }
 
             bridge::TokenTree::Punct(p) => {
-                let punct = tt::Punct {
+                let punct = Punct {
                     char: p.ch as char,
                     spacing: if p.joint { Spacing::Joint } else { Spacing::Alone },
                     span: p.span,
@@ -238,16 +233,17 @@ impl server::TokenStream for TokenIdServer {
             .map(|tree| match tree {
                 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
                     bridge::TokenTree::Ident(bridge::Ident {
-                        sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")),
-                        is_raw: ident.text.starts_with("r#"),
+                        sym: ident.sym,
+                        is_raw: ident.is_raw.yes(),
                         span: ident.span,
                     })
                 }
                 tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
                     bridge::TokenTree::Literal(bridge::Literal {
                         span: lit.span,
-                        ..server::FreeFunctions::literal_from_str(self, &lit.text)
-                            .unwrap_or_else(|_| panic!("`{}`", lit.text))
+                        kind: literal_kind_to_external(lit.kind),
+                        symbol: lit.symbol,
+                        suffix: lit.suffix,
                     })
                 }
                 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
@@ -364,11 +360,11 @@ impl server::Server for TokenIdServer {
     }
 
     fn intern_symbol(ident: &str) -> Self::Symbol {
-        Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident))
+        Symbol::intern(ident)
     }
 
     fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
-        f(symbol.text(&SYMBOL_INTERNER).as_str())
+        f(symbol.as_str())
     }
 }
 
@@ -381,12 +377,14 @@ mod tests {
         let s = TokenStream {
             token_trees: vec![
                 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                    text: "struct".into(),
+                    sym: Symbol::intern("struct"),
                     span: tt::TokenId(0),
+                    is_raw: tt::IdentIsRaw::No,
                 })),
                 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                    text: "T".into(),
+                    sym: Symbol::intern("T"),
                     span: tt::TokenId(0),
+                    is_raw: tt::IdentIsRaw::No,
                 })),
                 tt::TokenTree::Subtree(tt::Subtree {
                     delimiter: tt::Delimiter {
@@ -411,7 +409,8 @@ mod tests {
                 kind: tt::DelimiterKind::Parenthesis,
             },
             token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                text: "a".into(),
+                is_raw: tt::IdentIsRaw::No,
+                sym: Symbol::intern("a"),
                 span: tt::TokenId(0),
             }))]),
         });
@@ -428,8 +427,9 @@ mod tests {
         assert_eq!(
             underscore.token_trees[0],
             tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                text: "_".into(),
+                sym: Symbol::intern("_"),
                 span: tt::TokenId(0),
+                is_raw: tt::IdentIsRaw::No,
             }))
         );
     }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
index b1a448427c603..cdf93fa4251d5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
@@ -127,7 +127,8 @@ pub(super) mod token_stream {
     impl TokenStream {
         pub(crate) fn from_str(src: &str, call_site: S) -> Result, String> {
             let subtree =
-                mbe::parse_to_token_tree_static_span(call_site, src).ok_or("lexing error")?;
+                mbe::parse_to_token_tree_static_span(span::Edition::CURRENT_FIXME, call_site, src)
+                    .ok_or("lexing error")?;
 
             Ok(TokenStream::with_subtree(subtree))
         }
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 6334282538089..dc6e71163b2f2 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
@@ -21,20 +21,20 @@ fn test_derive_error() {
     assert_expand(
         "DeriveError",
         r#"struct S;"#,
-        expect![[r##"
+        expect![[r#"
             SUBTREE $$ 1 1
               IDENT   compile_error 1
               PUNCH   ! [alone] 1
               SUBTREE () 1 1
-                LITERAL "#[derive(DeriveError)] struct S ;"1
-              PUNCH   ; [alone] 1"##]],
-        expect![[r##"
+                LITERAL Str #[derive(DeriveError)] struct S ; 1
+              PUNCH   ; [alone] 1"#]],
+        expect![[r#"
             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"##]],
+                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#0
+              PUNCH   ; [alone] 42:2@0..100#0"#]],
     );
 }
 
@@ -47,18 +47,18 @@ fn test_fn_like_macro_noop() {
             SUBTREE $$ 1 1
               IDENT   ident 1
               PUNCH   , [alone] 1
-              LITERAL 01
+              LITERAL Integer 0 1
               PUNCH   , [alone] 1
-              LITERAL 11
+              LITERAL Integer 1 1
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
             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
+              LITERAL Integer 0 42:2@7..8#0
               PUNCH   , [alone] 42:2@8..9#0
-              LITERAL 142:2@10..11#0
+              LITERAL Integer 1 42:2@10..11#0
               PUNCH   , [alone] 42:2@11..12#0
               SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]],
     );
@@ -135,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.14f641
-              LITERAL 3.141
-              LITERAL 123i641
-              LITERAL 1231"#]],
+              LITERAL ByteStr byte_string 1
+              LITERAL Char c 1
+              LITERAL Str string 1
+              LITERAL Float 3.14f64 1
+              LITERAL Float 3.14 1
+              LITERAL Integer 123i64 1
+              LITERAL Integer 123 1"#]],
         expect![[r#"
             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"#]],
+              LITERAL ByteStr byte_string 42:2@0..100#0
+              LITERAL Char c 42:2@0..100#0
+              LITERAL Str string 42:2@0..100#0
+              LITERAL Float 3.14f64 42:2@0..100#0
+              LITERAL Float 3.14 42:2@0..100#0
+              LITERAL Integer 123i64 42:2@0..100#0
+              LITERAL Integer 123 42:2@0..100#0"#]],
     );
 }
 
@@ -175,50 +175,50 @@ fn test_fn_like_macro_clone_literals() {
     assert_expand(
         "fn_like_clone_tokens",
         r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###,
-        expect![[r###"
+        expect![[r#"
             SUBTREE $$ 1 1
-              LITERAL 1u161
+              LITERAL Integer 1u16 1
               PUNCH   , [alone] 1
-              LITERAL 2_u321
+              LITERAL Integer 2_u32 1
               PUNCH   , [alone] 1
               PUNCH   - [alone] 1
-              LITERAL 4i641
+              LITERAL Integer 4i64 1
               PUNCH   , [alone] 1
-              LITERAL 3.14f321
+              LITERAL Float 3.14f32 1
               PUNCH   , [alone] 1
-              LITERAL "hello bridge"1
+              LITERAL Str hello bridge 1
               PUNCH   , [alone] 1
-              LITERAL "suffixed"suffix1
+              LITERAL Str suffixedsuffix 1
               PUNCH   , [alone] 1
-              LITERAL r##"raw"##1
+              LITERAL StrRaw(2) raw 1
               PUNCH   , [alone] 1
-              LITERAL 'a'1
+              LITERAL Char a 1
               PUNCH   , [alone] 1
-              LITERAL b'b'1
+              LITERAL Byte b 1
               PUNCH   , [alone] 1
-              LITERAL c"null"1"###]],
-        expect![[r###"
+              LITERAL CStr null 1"#]],
+        expect![[r#"
             SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              LITERAL 1u1642:2@0..4#0
+              LITERAL Integer 1u16 42:2@0..4#0
               PUNCH   , [alone] 42:2@4..5#0
-              LITERAL 2_u3242:2@6..11#0
+              LITERAL Integer 2_u32 42:2@6..11#0
               PUNCH   , [alone] 42:2@11..12#0
               PUNCH   - [alone] 42:2@13..14#0
-              LITERAL 4i6442:2@14..18#0
+              LITERAL Integer 4i64 42:2@14..18#0
               PUNCH   , [alone] 42:2@18..19#0
-              LITERAL 3.14f3242:2@20..27#0
+              LITERAL Float 3.14f32 42:2@20..27#0
               PUNCH   , [alone] 42:2@27..28#0
-              LITERAL "hello bridge"42:2@29..43#0
+              LITERAL Str hello bridge 42:2@29..43#0
               PUNCH   , [alone] 42:2@43..44#0
-              LITERAL "suffixed"suffix42:2@45..61#0
+              LITERAL Str suffixedsuffix 42:2@45..61#0
               PUNCH   , [alone] 42:2@61..62#0
-              LITERAL r##"raw"##42:2@63..73#0
+              LITERAL StrRaw(2) raw 42:2@63..73#0
               PUNCH   , [alone] 42:2@73..74#0
-              LITERAL 'a'42:2@75..78#0
+              LITERAL Char a 42:2@75..78#0
               PUNCH   , [alone] 42:2@78..79#0
-              LITERAL b'b'42:2@80..84#0
+              LITERAL Byte b 42:2@80..84#0
               PUNCH   , [alone] 42:2@84..85#0
-              LITERAL c"null"42:2@86..93#0"###]],
+              LITERAL CStr null 42:2@86..93#0"#]],
     );
 }
 
@@ -231,20 +231,20 @@ fn test_attr_macro() {
         "attr_error",
         r#"mod m {}"#,
         r#"some arguments"#,
-        expect![[r##"
+        expect![[r#"
             SUBTREE $$ 1 1
               IDENT   compile_error 1
               PUNCH   ! [alone] 1
               SUBTREE () 1 1
-                LITERAL "#[attr_error(some arguments)] mod m {}"1
-              PUNCH   ; [alone] 1"##]],
-        expect![[r##"
+                LITERAL Str #[attr_error(some arguments)] mod m {} 1
+              PUNCH   ; [alone] 1"#]],
+        expect![[r#"
             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"##]],
+                LITERAL Str #[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 03b1117a5bd37..70eff51cadea8 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
@@ -2,14 +2,14 @@
 
 use expect_test::Expect;
 use proc_macro_api::msg::TokenId;
-use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId};
+use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId};
 use tt::TextRange;
 
 use crate::{dylib, proc_macro_test_dylib_path, EnvSnapshot, ProcMacroSrv};
 
 fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream {
     crate::server_impl::TokenStream::with_subtree(
-        mbe::parse_to_token_tree_static_span(call_site, src).unwrap(),
+        mbe::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src).unwrap(),
     )
 }
 
@@ -19,7 +19,7 @@ fn parse_string_spanned(
     src: &str,
 ) -> crate::server_impl::TokenStream {
     crate::server_impl::TokenStream::with_subtree(
-        mbe::parse_to_token_tree(anchor, call_site, src).unwrap(),
+        mbe::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(),
     )
 }
 
@@ -68,7 +68,7 @@ fn assert_expand_impl(
     let def_site = Span {
         range: TextRange::new(0.into(), 150.into()),
         anchor: SpanAnchor {
-            file_id: FileId::from_raw(41),
+            file_id: EditionedFileId::current_edition(FileId::from_raw(41)),
             ast_id: ErasedFileAstId::from_raw(From::from(1)),
         },
         ctx: SyntaxContextId::ROOT,
@@ -76,7 +76,7 @@ fn assert_expand_impl(
     let call_site = Span {
         range: TextRange::new(0.into(), 100.into()),
         anchor: SpanAnchor {
-            file_id: FileId::from_raw(42),
+            file_id: EditionedFileId::current_edition(FileId::from_raw(42)),
             ast_id: ErasedFileAstId::from_raw(From::from(2)),
         },
         ctx: SyntaxContextId::ROOT,
diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
index 097ee1f75cd75..8b34bd3fad1a1 100644
--- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
@@ -25,6 +25,7 @@ itertools.workspace = true
 
 # local deps
 base-db.workspace = true
+intern.workspace = true
 span.workspace = true
 cfg.workspace = true
 paths = { workspace = true, features = ["serde1"] }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs
index b409bc1ce7ad1..e921e3de722d1 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs
@@ -4,6 +4,7 @@
 use std::{fmt, str::FromStr};
 
 use cfg::{CfgDiff, CfgOptions};
+use intern::Symbol;
 use rustc_hash::FxHashMap;
 use serde::Serialize;
 
@@ -44,8 +45,10 @@ impl Extend for CfgOptions {
     fn extend>(&mut self, iter: T) {
         for cfg_flag in iter {
             match cfg_flag {
-                CfgFlag::Atom(it) => self.insert_atom(it.into()),
-                CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()),
+                CfgFlag::Atom(it) => self.insert_atom(Symbol::intern(&it)),
+                CfgFlag::KeyValue { key, value } => {
+                    self.insert_key_value(Symbol::intern(&key), Symbol::intern(&value))
+                }
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs
index 88fb10a68c617..049acc290bbfe 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/env.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs
@@ -75,14 +75,26 @@ pub(crate) fn cargo_config_env(
     }
     // if successful we receive `env.key.value = "value" per entry
     tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
-    utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default()
+    utf8_stdout(cargo_config)
+        .map(parse_output_cargo_config_env)
+        .inspect(|env| {
+            tracing::debug!("Discovered cargo config env: {:?}", env);
+        })
+        .inspect_err(|err| {
+            tracing::debug!("Failed to discover cargo config env: {:?}", err);
+        })
+        .unwrap_or_default()
 }
 
 fn parse_output_cargo_config_env(stdout: String) -> FxHashMap {
     stdout
         .lines()
         .filter_map(|l| l.strip_prefix("env."))
-        .filter_map(|l| l.split_once(".value = "))
+        .filter_map(|l| {
+            l.split_once(" = ")
+                // cargo used to report it with this, keep it for a couple releases around
+                .or_else(|| l.split_once(".value = "))
+        })
         .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned()))
         .collect()
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 4a916e570be27..cf0a6ad402508 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -224,6 +224,7 @@ impl ProjectJson {
                     Crate {
                         display_name: crate_data
                             .display_name
+                            .as_deref()
                             .map(CrateDisplayName::from_canonical_name),
                         root_module,
                         edition: crate_data.edition.into(),
@@ -275,17 +276,32 @@ impl ProjectJson {
         self.manifest.as_ref()
     }
 
+    pub fn crate_by_buildfile(&self, path: &AbsPath) -> Option {
+        // this is fast enough for now, but it's unfortunate that this is O(crates).
+        let path: &std::path::Path = path.as_ref();
+        self.crates
+            .iter()
+            .filter(|krate| krate.is_workspace_member)
+            .filter_map(|krate| krate.build.clone())
+            .find(|build| build.build_file.as_std_path() == path)
+    }
+
     /// Returns the path to the project's manifest or root folder, if no manifest exists.
     pub fn manifest_or_root(&self) -> &AbsPath {
         self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref())
     }
 
+    /// Returns the path to the project's root folder.
+    pub fn project_root(&self) -> &AbsPath {
+        &self.project_root
+    }
+
     pub fn runnables(&self) -> &[Runnable] {
         &self.runnables
     }
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
 pub struct ProjectJsonData {
     sysroot: Option,
     sysroot_src: Option,
@@ -294,7 +310,7 @@ pub struct ProjectJsonData {
     runnables: Vec,
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
 struct CrateData {
     display_name: Option,
     root_module: Utf8PathBuf,
@@ -318,7 +334,7 @@ struct CrateData {
     build: Option,
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
 #[serde(rename = "edition")]
 enum EditionData {
     #[serde(rename = "2015")]
@@ -331,7 +347,7 @@ enum EditionData {
     Edition2024,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
 pub struct BuildData {
     label: String,
     build_file: Utf8PathBuf,
@@ -418,7 +434,7 @@ pub(crate) struct Dep {
     pub(crate) name: CrateName,
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
 struct CrateSource {
     include_dirs: Vec,
     exclude_dirs: Vec,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 2762de5997ae9..8f5457bf99af9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -1,12 +1,14 @@
 use std::ops::Deref;
 
-use base_db::{CrateGraph, FileId, ProcMacroPaths};
+use base_db::{CrateGraph, ProcMacroPaths};
 use cargo_metadata::Metadata;
 use cfg::{CfgAtom, CfgDiff};
 use expect_test::{expect_file, ExpectFile};
+use intern::sym;
 use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
 use rustc_hash::FxHashMap;
 use serde::de::DeserializeOwned;
+use span::FileId;
 use triomphe::Arc;
 
 use crate::{
@@ -180,7 +182,7 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
 #[test]
 fn cargo_hello_world_project_model_with_wildcard_overrides() {
     let cfg_overrides = CfgOverrides {
-        global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
+        global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
         selective: Default::default(),
     };
     let (crate_graph, _proc_macros) =
@@ -199,7 +201,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
         global: Default::default(),
         selective: std::iter::once((
             "libc".to_owned(),
-            CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
+            CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
         ))
         .collect(),
     };
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 5e27ce298735e..31d1c77fd07c4 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -6,14 +6,15 @@ use std::{collections::VecDeque, fmt, fs, iter, sync};
 
 use anyhow::Context;
 use base_db::{
-    CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId,
+    CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env,
     LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
+use intern::{sym, Symbol};
 use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
-use span::Edition;
+use span::{Edition, FileId};
 use toolchain::Tool;
 use tracing::instrument;
 use triomphe::Arc;
@@ -30,6 +31,7 @@ use crate::{
     utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package,
     ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
 };
+use tracing::{debug, error, info};
 
 pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option;
 
@@ -249,7 +251,7 @@ impl ProjectWorkspace {
                 };
 
                 let rustc =  rustc_dir.and_then(|rustc_dir| {
-                    tracing::info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
+                    info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
                     match CargoWorkspace::fetch_metadata(
                         &rustc_dir,
                         cargo_toml.parent(),
@@ -521,6 +523,14 @@ impl ProjectWorkspace {
         }
     }
 
+    pub fn workspace_root(&self) -> &AbsPath {
+        match &self.kind {
+            ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.workspace_root(),
+            ProjectWorkspaceKind::Json(project) => project.project_root(),
+            ProjectWorkspaceKind::DetachedFile { file, .. } => file.parent(),
+        }
+    }
+
     pub fn manifest(&self) -> Option<&ManifestPath> {
         match &self.kind {
             ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo.manifest_path()),
@@ -766,9 +776,9 @@ impl ProjectWorkspace {
         };
 
         if matches!(sysroot.mode(), SysrootMode::Stitched(_)) && crate_graph.patch_cfg_if() {
-            tracing::debug!("Patched std to depend on cfg-if")
+            debug!("Patched std to depend on cfg-if")
         } else {
-            tracing::debug!("Did not patch std to depend on cfg-if")
+            debug!("Did not patch std to depend on cfg-if")
         }
         (crate_graph, proc_macros)
     }
@@ -893,7 +903,10 @@ fn project_json_to_crate_graph(
                     .collect();
                 override_cfg.apply(
                     &mut cfg_options,
-                    display_name.as_ref().map(|it| it.canonical_name()).unwrap_or_default(),
+                    display_name
+                        .as_ref()
+                        .map(|it| it.canonical_name().as_str())
+                        .unwrap_or_default(),
                 );
                 let crate_graph_crate_id = crate_graph.add_crate_root(
                     file_id,
@@ -913,10 +926,18 @@ fn project_json_to_crate_graph(
                         CrateOrigin::Local { repo: None, name: None }
                     },
                 );
+                debug!(
+                    ?crate_graph_crate_id,
+                    crate = display_name.as_ref().map(|name| name.canonical_name().as_str()),
+                    "added root to crate graph"
+                );
                 if *is_proc_macro {
                     if let Some(path) = proc_macro_dylib_path.clone() {
                         let node = Ok((
-                            display_name.as_ref().map(|it| it.canonical_name().to_owned()),
+                            display_name
+                                .as_ref()
+                                .map(|it| it.canonical_name().as_str().to_owned())
+                                .unwrap_or_else(|| format!("crate{}", idx.0)),
                             path,
                         ));
                         proc_macros.insert(crate_graph_crate_id, node);
@@ -927,6 +948,7 @@ fn project_json_to_crate_graph(
         )
         .collect();
 
+    debug!(map = ?idx_to_crate_id);
     for (from_idx, krate) in project.crates() {
         if let Some(&from) = idx_to_crate_id.get(&from_idx) {
             public_deps.add_to_crate_graph(crate_graph, from);
@@ -977,8 +999,8 @@ fn cargo_to_crate_graph(
 
             if cargo[pkg].is_local {
                 // Add test cfg for local crates
-                cfg_options.insert_atom("test".into());
-                cfg_options.insert_atom("rust_analyzer".into());
+                cfg_options.insert_atom(sym::test.clone());
+                cfg_options.insert_atom(sym::rust_analyzer.clone());
             }
 
             override_cfg.apply(&mut cfg_options, &cargo[pkg].name);
@@ -1013,12 +1035,12 @@ fn cargo_to_crate_graph(
                 if pkg_data.is_local {
                     CrateOrigin::Local {
                         repo: pkg_data.repository.clone(),
-                        name: Some(pkg_data.name.clone()),
+                        name: Some(Symbol::intern(&pkg_data.name)),
                     }
                 } else {
                     CrateOrigin::Library {
                         repo: pkg_data.repository.clone(),
-                        name: pkg_data.name.clone(),
+                        name: Symbol::intern(&pkg_data.name),
                     }
                 },
             );
@@ -1144,21 +1166,19 @@ fn detached_file_to_crate_graph(
         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
 
     let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
-    cfg_options.insert_atom("test".into());
-    cfg_options.insert_atom("rust_analyzer".into());
+    cfg_options.insert_atom(sym::test.clone());
+    cfg_options.insert_atom(sym::rust_analyzer.clone());
     override_cfg.apply(&mut cfg_options, "");
     let cfg_options = Arc::new(cfg_options);
 
     let file_id = match load(detached_file) {
         Some(file_id) => file_id,
         None => {
-            tracing::error!("Failed to load detached file {:?}", detached_file);
+            error!("Failed to load detached file {:?}", detached_file);
             return (crate_graph, FxHashMap::default());
         }
     };
-    let display_name = detached_file
-        .file_stem()
-        .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
+    let display_name = detached_file.file_stem().map(CrateDisplayName::from_canonical_name);
     let detached_file_crate = crate_graph.add_crate_root(
         file_id,
         Edition::CURRENT,
@@ -1231,7 +1251,7 @@ fn handle_rustc_crates(
                         file_id,
                         &rustc_workspace[tgt].name,
                         kind,
-                        CrateOrigin::Rustc { name: rustc_workspace[pkg].name.clone() },
+                        CrateOrigin::Rustc { name: Symbol::intern(&rustc_workspace[pkg].name) },
                     );
                     pkg_to_lib_crate.insert(pkg, crate_id);
                     // Add dependencies on core / std / alloc for this crate
@@ -1307,7 +1327,7 @@ fn add_target_crate_root(
     let cfg_options = {
         let mut opts = cfg_options;
         for feature in pkg.active_features.iter() {
-            opts.insert_key_value("feature".into(), feature.into());
+            opts.insert_key_value(sym::feature.clone(), Symbol::intern(feature));
         }
         if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
             opts.extend(cfgs.iter().cloned());
@@ -1328,7 +1348,7 @@ fn add_target_crate_root(
     let crate_id = crate_graph.add_crate_root(
         file_id,
         edition,
-        Some(CrateDisplayName::from_canonical_name(cargo_name.to_owned())),
+        Some(CrateDisplayName::from_canonical_name(cargo_name)),
         Some(pkg.version.to_string()),
         Arc::new(cfg_options),
         potential_cfg_options.map(Arc::new),
@@ -1338,8 +1358,8 @@ fn add_target_crate_root(
     );
     if let TargetKind::Lib { is_proc_macro: true } = kind {
         let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
-            Some(it) => it.cloned().map(|path| Ok((Some(cargo_name.to_owned()), path))),
-            None => Some(Err("crate has not yet been built".to_owned())),
+            Some(it) => it.cloned().map(|path| Ok((cargo_name.to_owned(), path))),
+            None => Some(Err("proc-macro crate is missing its build data".to_owned())),
         };
         if let Some(proc_macro) = proc_macro {
             proc_macros.insert(crate_id, proc_macro);
@@ -1349,7 +1369,7 @@ fn add_target_crate_root(
     crate_id
 }
 
-#[derive(Default)]
+#[derive(Default, Debug)]
 struct SysrootPublicDeps {
     deps: Vec<(CrateName, CrateId, bool)>,
 }
@@ -1381,8 +1401,8 @@ fn sysroot_to_crate_graph(
                 &CfgOverrides {
                     global: CfgDiff::new(
                         vec![
-                            CfgAtom::Flag("debug_assertions".into()),
-                            CfgAtom::Flag("miri".into()),
+                            CfgAtom::Flag(sym::debug_assertions.clone()),
+                            CfgAtom::Flag(sym::miri.clone()),
                         ],
                         vec![],
                     )
@@ -1394,14 +1414,14 @@ fn sysroot_to_crate_graph(
 
             let mut pub_deps = vec![];
             let mut libproc_macro = None;
-            let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap();
+            let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
             for (cid, c) in cg.iter_mut() {
                 // uninject `test` flag so `core` keeps working.
                 Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
                 // patch the origin
                 if c.origin.is_local() {
                     let lang_crate = LangCrateOrigin::from(
-                        c.display_name.as_ref().map_or("", |it| it.canonical_name()),
+                        c.display_name.as_ref().map_or("", |it| it.canonical_name().as_str()),
                     );
                     c.origin = CrateOrigin::Lang(lang_crate);
                     match lang_crate {
@@ -1449,8 +1469,8 @@ fn sysroot_to_crate_graph(
             let cfg_options = Arc::new({
                 let mut cfg_options = CfgOptions::default();
                 cfg_options.extend(rustc_cfg);
-                cfg_options.insert_atom("debug_assertions".into());
-                cfg_options.insert_atom("miri".into());
+                cfg_options.insert_atom(sym::debug_assertions.clone());
+                cfg_options.insert_atom(sym::miri.clone());
                 cfg_options
             });
             let sysroot_crates: FxHashMap = stitched
@@ -1458,11 +1478,10 @@ fn sysroot_to_crate_graph(
                 .filter_map(|krate| {
                     let file_id = load(&stitched[krate].root)?;
 
-                    let display_name =
-                        CrateDisplayName::from_canonical_name(stitched[krate].name.clone());
+                    let display_name = CrateDisplayName::from_canonical_name(&stitched[krate].name);
                     let crate_id = crate_graph.add_crate_root(
                         file_id,
-                        Edition::CURRENT,
+                        Edition::CURRENT_FIXME,
                         Some(display_name),
                         None,
                         cfg_options.clone(),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 93fb55ede8ea2..bc1b13a649700 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -54,6 +54,7 @@ hir-def.workspace = true
 hir-ty.workspace = true
 hir.workspace = true
 ide-db.workspace = true
+intern.workspace = true
 # This should only be used in CLI
 ide-ssr.workspace = true
 ide.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 1985093bc5c34..6a980a153c991 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
@@ -175,6 +175,7 @@ fn run_server() -> anyhow::Result<()> {
             return Err(e.into());
         }
     };
+
     tracing::info!("InitializeParams: {}", initialize_params);
     let lsp_types::InitializeParams {
         root_uri,
@@ -264,7 +265,10 @@ fn run_server() -> anyhow::Result<()> {
         return Err(e.into());
     }
 
-    if !config.has_linked_projects() && config.detached_files().is_empty() {
+    if config.discover_workspace_config().is_none()
+        && !config.has_linked_projects()
+        && config.detached_files().is_empty()
+    {
         config.rediscover_workspaces();
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs
index 212294b5d326e..9610808c27e18 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs
@@ -67,7 +67,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
         code_action_provider: Some(config.caps().code_action_capabilities()),
         code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
         document_formatting_provider: Some(OneOf::Left(true)),
-        document_range_formatting_provider: match config.rustfmt() {
+        document_range_formatting_provider: match config.rustfmt(None) {
             RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
             _ => Some(OneOf::Left(false)),
         },
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 90316f3b89dfa..380105d2c2141 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
@@ -25,7 +25,7 @@ use ide_db::{
         salsa::{self, debug::DebugQueryTable, ParallelDatabase},
         SourceDatabase, SourceDatabaseExt,
     },
-    LineIndexDatabase, SnippetCap,
+    EditionedFileId, LineIndexDatabase, SnippetCap,
 };
 use itertools::Itertools;
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -35,7 +35,7 @@ use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSourc
 use rayon::prelude::*;
 use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{AstNode, SyntaxNode};
-use vfs::{AbsPathBuf, FileId, Vfs, VfsPath};
+use vfs::{AbsPathBuf, Vfs, VfsPath};
 
 use crate::cli::{
     flags::{self, OutputFormat},
@@ -120,7 +120,7 @@ impl flags::AnalysisStats {
                 for file_id in source_root.iter() {
                     if let Some(p) = source_root.path_for_file(&file_id) {
                         if let Some((_, Some("rs"))) = p.name_and_extension() {
-                            db.file_item_tree(file_id.into());
+                            db.file_item_tree(EditionedFileId::current_edition(file_id).into());
                             num_item_trees += 1;
                         }
                     }
@@ -140,7 +140,7 @@ impl flags::AnalysisStats {
             let module = krate.root_module();
             let file_id = module.definition_source_file_id(db);
             let file_id = file_id.original_file(db);
-            let source_root = db.file_source_root(file_id);
+            let source_root = db.file_source_root(file_id.into());
             let source_root = db.source_root(source_root);
             if !source_root.is_library || self.with_deps {
                 num_crates += 1;
@@ -332,7 +332,7 @@ impl flags::AnalysisStats {
         ws: &ProjectWorkspace,
         db: &RootDatabase,
         vfs: &Vfs,
-        mut file_ids: Vec,
+        mut file_ids: Vec,
         verbosity: Verbosity,
     ) {
         let cargo_config = CargoConfig {
@@ -367,11 +367,10 @@ impl flags::AnalysisStats {
 
         for &file_id in &file_ids {
             let sema = hir::Semantics::new(db);
-            let _ = db.parse(file_id);
 
-            let parse = sema.parse(file_id);
-            let file_txt = db.file_text(file_id);
-            let path = vfs.file_path(file_id).as_path().unwrap();
+            let parse = sema.parse_guess_edition(file_id.into());
+            let file_txt = db.file_text(file_id.into());
+            let path = vfs.file_path(file_id.into()).as_path().unwrap();
 
             for node in parse.syntax().descendants() {
                 let expr = match syntax::ast::Expr::cast(node.clone()) {
@@ -398,7 +397,7 @@ impl flags::AnalysisStats {
 
                 let range = sema.original_range(expected_tail.syntax()).range;
                 let original_text: String = db
-                    .file_text(file_id)
+                    .file_text(file_id.into())
                     .chars()
                     .skip(usize::from(range.start()))
                     .take(usize::from(range.end()) - usize::from(range.start()))
@@ -423,7 +422,7 @@ impl flags::AnalysisStats {
                 if found_terms.is_empty() {
                     acc.tail_expr_no_term += 1;
                     acc.total_tail_exprs += 1;
-                    // println!("\n{}\n", &original_text);
+                    // println!("\n{original_text}\n");
                     continue;
                 };
 
@@ -621,7 +620,7 @@ impl flags::AnalysisStats {
                 module
                     .krate()
                     .display_name(db)
-                    .map(|it| it.canonical_name().to_owned())
+                    .map(|it| it.canonical_name().as_str().to_owned())
                     .into_iter()
                     .chain(
                         module
@@ -650,7 +649,7 @@ impl flags::AnalysisStats {
                     };
                     if let Some(src) = source {
                         let original_file = src.file_id.original_file(db);
-                        let path = vfs.file_path(original_file);
+                        let path = vfs.file_path(original_file.into());
                         let syntax_range = src.text_range();
                         format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
                     } else {
@@ -664,8 +663,10 @@ impl flags::AnalysisStats {
                 bar.println(msg());
             }
             bar.set_message(msg);
-            let (body, sm) = db.body_with_source_map(body_id.into());
+            let body = db.body(body_id.into());
             let inference_result = db.infer(body_id.into());
+            // This query is LRU'd, so actually calling it will skew the timing results.
+            let sm = || db.body_with_source_map(body_id.into()).1;
 
             // region:expressions
             let (previous_exprs, previous_unknown, previous_partially_unknown) =
@@ -676,7 +677,8 @@ impl flags::AnalysisStats {
                 let unknown_or_partial = if ty.is_unknown() {
                     num_exprs_unknown += 1;
                     if verbosity.is_spammy() {
-                        if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
+                        if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm(), expr_id)
+                        {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Unknown type",
                                 path,
@@ -700,7 +702,7 @@ impl flags::AnalysisStats {
                 };
                 if self.only.is_some() && verbosity.is_spammy() {
                     // in super-verbose mode for just one function, we print every single expression
-                    if let Some((_, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
+                    if let Some((_, start, end)) = expr_syntax_range(db, vfs, &sm(), expr_id) {
                         bar.println(format!(
                             "{}:{}-{}:{}: {}",
                             start.line + 1,
@@ -716,14 +718,15 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv_expr(db, vfs, &sm, expr_id),
+                        location_csv_expr(db, vfs, &sm(), expr_id),
                         ty.display(db)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
                     num_expr_type_mismatches += 1;
                     if verbosity.is_verbose() {
-                        if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
+                        if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm(), expr_id)
+                        {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Expected {}, got {}",
                                 path,
@@ -746,7 +749,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv_expr(db, vfs, &sm, expr_id),
+                            location_csv_expr(db, vfs, &sm(), expr_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -773,7 +776,7 @@ impl flags::AnalysisStats {
                 let unknown_or_partial = if ty.is_unknown() {
                     num_pats_unknown += 1;
                     if verbosity.is_spammy() {
-                        if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
+                        if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm(), pat_id) {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Unknown type",
                                 path,
@@ -797,7 +800,7 @@ impl flags::AnalysisStats {
                 };
                 if self.only.is_some() && verbosity.is_spammy() {
                     // in super-verbose mode for just one function, we print every single pattern
-                    if let Some((_, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
+                    if let Some((_, start, end)) = pat_syntax_range(db, vfs, &sm(), pat_id) {
                         bar.println(format!(
                             "{}:{}-{}:{}: {}",
                             start.line + 1,
@@ -813,14 +816,14 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv_pat(db, vfs, &sm, pat_id),
+                        location_csv_pat(db, vfs, &sm(), pat_id),
                         ty.display(db)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
                     num_pat_type_mismatches += 1;
                     if verbosity.is_verbose() {
-                        if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
+                        if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm(), pat_id) {
                             bar.println(format!(
                                 "{} {}:{}-{}:{}: Expected {}, got {}",
                                 path,
@@ -843,7 +846,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv_pat(db, vfs, &sm, pat_id),
+                            location_csv_pat(db, vfs, &sm(), pat_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -912,7 +915,7 @@ impl flags::AnalysisStats {
                 module
                     .krate()
                     .display_name(db)
-                    .map(|it| it.canonical_name().to_owned())
+                    .map(|it| it.canonical_name().as_str().to_owned())
                     .into_iter()
                     .chain(
                         module
@@ -944,7 +947,7 @@ impl flags::AnalysisStats {
                     };
                     if let Some(src) = source {
                         let original_file = src.file_id.original_file(db);
-                        let path = vfs.file_path(original_file);
+                        let path = vfs.file_path(original_file.into());
                         let syntax_range = src.text_range();
                         format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
                     } else {
@@ -958,7 +961,7 @@ impl flags::AnalysisStats {
                 bar.println(msg());
             }
             bar.set_message(msg);
-            db.body_with_source_map(body_id.into());
+            db.body(body_id.into());
             bar.inc(1);
         }
 
@@ -968,7 +971,7 @@ impl flags::AnalysisStats {
         report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms");
     }
 
-    fn run_ide_things(&self, analysis: Analysis, mut file_ids: Vec) {
+    fn run_ide_things(&self, analysis: Analysis, mut file_ids: Vec) {
         file_ids.sort();
         file_ids.dedup();
         let mut sw = self.stop_watch();
@@ -998,7 +1001,7 @@ impl flags::AnalysisStats {
                     term_search_borrowck: true,
                 },
                 ide::AssistResolveStrategy::All,
-                file_id,
+                file_id.into(),
             );
         }
         for &file_id in &file_ids {
@@ -1031,7 +1034,7 @@ impl flags::AnalysisStats {
                     fields_to_resolve: InlayFieldsToResolve::empty(),
                     range_exclusive_hints: true,
                 },
-                file_id,
+                file_id.into(),
                 None,
             );
         }
@@ -1047,7 +1050,7 @@ impl flags::AnalysisStats {
                         annotate_enum_variant_references: false,
                         location: ide::AnnotationLocation::AboveName,
                     },
-                    file_id,
+                    file_id.into(),
                 )
                 .unwrap()
                 .into_iter()
@@ -1072,8 +1075,8 @@ 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_rooted(db);
-    let path = vfs.file_path(original_range.file_id);
-    let line_index = db.line_index(original_range.file_id);
+    let path = vfs.file_path(original_range.file_id.into());
+    let line_index = db.line_index(original_range.file_id.into());
     let text_range = original_range.range;
     let (start, end) =
         (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -1088,8 +1091,8 @@ 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_rooted(db);
-    let path = vfs.file_path(original_range.file_id);
-    let line_index = db.line_index(original_range.file_id);
+    let path = vfs.file_path(original_range.file_id.into());
+    let line_index = db.line_index(original_range.file_id.into());
     let text_range = original_range.range;
     let (start, end) =
         (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -1107,8 +1110,8 @@ fn expr_syntax_range<'a>(
         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_rooted(db);
-        let path = vfs.file_path(original_range.file_id);
-        let line_index = db.line_index(original_range.file_id);
+        let path = vfs.file_path(original_range.file_id.into());
+        let line_index = db.line_index(original_range.file_id.into());
         let text_range = original_range.range;
         let (start, end) =
             (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -1128,8 +1131,8 @@ fn pat_syntax_range<'a>(
         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_rooted(db);
-        let path = vfs.file_path(original_range.file_id);
-        let line_index = db.line_index(original_range.file_id);
+        let path = vfs.file_path(original_range.file_id.into());
+        let line_index = db.line_index(original_range.file_id.into());
         let text_range = original_range.range;
         let (start, end) =
             (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index d5eac49ad3a5b..4ddeb4ab1b0c3 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -5,8 +5,8 @@ use project_model::{CargoConfig, RustLibSource};
 use rustc_hash::FxHashSet;
 
 use hir::{db::HirDatabase, Crate, HirFileIdExt, Module};
-use ide::{AnalysisHost, AssistResolveStrategy, DiagnosticsConfig, Severity};
-use ide_db::base_db::SourceDatabaseExt;
+use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity};
+use ide_db::{base_db::SourceDatabaseExt, LineIndexDatabase};
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
 
 use crate::cli::flags;
@@ -48,7 +48,7 @@ impl flags::Diagnostics {
 
         let work = all_modules(db).into_iter().filter(|module| {
             let file_id = module.definition_source_file_id(db).original_file(db);
-            let source_root = db.file_source_root(file_id);
+            let source_root = db.file_source_root(file_id.into());
             let source_root = db.source_root(source_root);
             !source_root.is_library
         });
@@ -58,12 +58,15 @@ impl flags::Diagnostics {
             if !visited_files.contains(&file_id) {
                 let crate_name =
                     module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned();
-                println!("processing crate: {crate_name}, module: {}", _vfs.file_path(file_id));
+                println!(
+                    "processing crate: {crate_name}, module: {}",
+                    _vfs.file_path(file_id.into())
+                );
                 for diagnostic in analysis
                     .diagnostics(
                         &DiagnosticsConfig::test_sample(),
                         AssistResolveStrategy::None,
-                        file_id,
+                        file_id.into(),
                     )
                     .unwrap()
                 {
@@ -71,7 +74,11 @@ impl flags::Diagnostics {
                         found_error = true;
                     }
 
-                    println!("{diagnostic:?}");
+                    let Diagnostic { code, message, range, severity, .. } = diagnostic;
+                    let line_index = db.line_index(range.file_id);
+                    let start = line_index.line_col(range.range.start());
+                    let end = line_index.line_col(range.range.end());
+                    println!("{severity:?} {code:?} from {start:?} to {end:?}: {message}");
                 }
 
                 visited_files.insert(file_id);
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 8f60b17b5943f..ee134b6c507e8 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
@@ -342,7 +342,7 @@ mod test {
         let (file_id, range_or_offset) =
             change_fixture.file_position.expect("expected a marker ()");
         let offset = range_or_offset.expect_offset();
-        (host, FilePosition { file_id, offset })
+        (host, FilePosition { file_id: file_id.into(), offset })
     }
 
     /// If expected == "", then assert that there are no symbols (this is basically local symbol)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index 28cbd1afd8cfd..7f24fa2835e93 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -1,6 +1,7 @@
 //! Applies structured search replace rules from the command line.
 
 use anyhow::Context;
+use ide_db::EditionedFileId;
 use ide_ssr::MatchFinder;
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
 use project_model::{CargoConfig, RustLibSource};
@@ -67,7 +68,10 @@ impl flags::Search {
             for &root in db.local_roots().iter() {
                 let sr = db.source_root(root);
                 for file_id in sr.iter() {
-                    for debug_info in match_finder.debug_where_text_equal(file_id, debug_snippet) {
+                    for debug_info in match_finder.debug_where_text_equal(
+                        EditionedFileId::current_edition(file_id),
+                        debug_snippet,
+                    ) {
                         println!("{debug_info:#?}");
                     }
                 }
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 3594cdda2e962..b9b8cfdfc9eab 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -8,6 +8,7 @@ use std::{fmt, iter, ops::Not, sync::OnceLock};
 use cfg::{CfgAtom, CfgDiff};
 use dirs::config_dir;
 use flycheck::{CargoOptions, FlycheckConfig};
+use hir::Symbol;
 use ide::{
     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
     GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat,
@@ -107,7 +108,7 @@ config_data! {
         /// targets and features, with the following base command line:
         ///
         /// ```bash
-        /// cargo check --quiet --workspace --message-format=json --all-targets
+        /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going
         /// ```
         /// .
         cargo_buildScripts_overrideCommand: Option> = None,
@@ -282,9 +283,9 @@ config_data! {
         linkedProjects: Vec = vec![],
 
         /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
-        lru_capacity: Option                 = None,
+        lru_capacity: Option                 = None,
         /// Sets the LRU capacity of the specified queries.
-        lru_query_capacities: FxHashMap, usize> = FxHashMap::default(),
+        lru_query_capacities: FxHashMap, u16> = FxHashMap::default(),
 
         /// These proc-macros will be ignored when trying to expand them.
         ///
@@ -327,6 +328,101 @@ config_data! {
         /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
         /// available on a nightly build.
         rustfmt_rangeFormatting_enable: bool = false,
+
+        /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+        ///
+        /// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.
+        /// `progress_label` is used for the title in progress indicators, whereas `files_to_watch`
+        /// is used to determine which build system-specific files should be watched in order to
+        /// reload rust-analyzer.
+        ///
+        /// Below is an example of a valid configuration:
+        /// ```json
+        /// "rust-analyzer.workspace.discoverConfig": {
+        ///     "command": [
+        ///         "rust-project",
+        ///         "develop-json"
+        ///     ],
+        ///     "progressLabel": "rust-analyzer",
+        ///     "filesToWatch": [
+        ///         "BUCK"
+        ///     ]
+        /// }
+        /// ```
+        ///
+        /// ## On `DiscoverWorkspaceConfig::command`
+        ///
+        /// **Warning**: This format is provisional and subject to change.
+        ///
+        /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object
+        /// corresponding to `DiscoverProjectData::Finished`:
+        ///
+        /// ```norun
+        /// #[derive(Debug, Clone, Deserialize, Serialize)]
+        /// #[serde(tag = "kind")]
+        /// #[serde(rename_all = "snake_case")]
+        /// enum DiscoverProjectData {
+        ///     Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },
+        ///     Error { error: String, source: Option },
+        ///     Progress { message: String },
+        /// }
+        /// ```
+        ///
+        /// As JSON, `DiscoverProjectData::Finished` is:
+        ///
+        /// ```json
+        /// {
+        ///     // the internally-tagged representation of the enum.
+        ///     "kind": "finished",
+        ///     // the file used by a non-Cargo build system to define
+        ///     // a package or target.
+        ///     "buildfile": "rust-analyzer/BUILD",
+        ///     // the contents of a rust-project.json, elided for brevity
+        ///     "project": {
+        ///         "sysroot": "foo",
+        ///         "crates": []
+        ///     }
+        /// }
+        /// ```
+        ///
+        /// It is encouraged, but not required, to use the other variants on
+        /// `DiscoverProjectData` to provide a more polished end-user experience.
+        ///
+        /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,
+        /// which will be substituted with the JSON-serialized form of the following
+        /// enum:
+        ///
+        /// ```norun
+        /// #[derive(PartialEq, Clone, Debug, Serialize)]
+        /// #[serde(rename_all = "camelCase")]
+        /// pub enum DiscoverArgument {
+        ///    Path(AbsPathBuf),
+        ///    Buildfile(AbsPathBuf),
+        /// }
+        /// ```
+        ///
+        /// The JSON representation of `DiscoverArgument::Path` is:
+        ///
+        /// ```json
+        /// {
+        ///     "path": "src/main.rs"
+        /// }
+        /// ```
+        ///
+        /// Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
+        ///
+        /// ```
+        /// {
+        ///     "buildfile": "BUILD"
+        /// }
+        /// ```
+        ///
+        /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`,
+        /// and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to
+        /// to update an existing workspace. As a reference for implementors,
+        /// buck2's `rust-project` will likely be useful:
+        /// https://github.com/facebook/buck2/tree/main/integrations/rust-project.
+        workspace_discoverConfig: Option = None,
     }
 }
 
@@ -510,9 +606,9 @@ config_data! {
         /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
         inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix,
         /// Whether to show const generic parameter name inlay hints.
-        inlayHints_genericParameterHints_const_enable: bool= false,
+        inlayHints_genericParameterHints_const_enable: bool= true,
         /// Whether to show generic lifetime parameter name inlay hints.
-        inlayHints_genericParameterHints_lifetime_enable: bool = true,
+        inlayHints_genericParameterHints_lifetime_enable: bool = false,
         /// Whether to show generic type parameter name inlay hints.
         inlayHints_genericParameterHints_type_enable: bool = false,
         /// Whether to show implicit drop hints.
@@ -558,9 +654,6 @@ config_data! {
         lens_debug_enable: bool            = true,
         /// Whether to show CodeLens in Rust files.
         lens_enable: bool           = true,
-        /// Internal config: use custom client-side commands even when the
-        /// client doesn't set the corresponding capability.
-        lens_forceCustomCommands: bool = true,
         /// Whether to show `Implementations` lens. Only applies when
         /// `#rust-analyzer.lens.enable#` is set.
         lens_implementations_enable: bool  = true,
@@ -585,9 +678,6 @@ config_data! {
         /// Whether to show `can't find Cargo.toml` error message.
         notifications_cargoTomlNotFound: bool      = true,
 
-        /// Whether to send an UnindexedProject notification to the client.
-        notifications_unindexedProject: bool      = false,
-
         /// How many worker threads in the main loop. The default `null` means to pick automatically.
         numThreads: Option = None,
 
@@ -660,6 +750,20 @@ config_data! {
     }
 }
 
+#[derive(Debug)]
+pub enum RatomlFileKind {
+    Workspace,
+    Crate,
+}
+
+#[derive(Debug, Clone)]
+// FIXME @alibektas : Seems like a clippy warning of this sort should tell that combining different ConfigInputs into one enum was not a good idea.
+#[allow(clippy::large_enum_variant)]
+enum RatomlFile {
+    Workspace(GlobalLocalConfigInput),
+    Crate(LocalConfigInput),
+}
+
 #[derive(Debug, Clone)]
 pub struct Config {
     discovered_projects: Vec,
@@ -685,22 +789,20 @@ pub struct Config {
     /// | Windows | `{FOLDERID_RoamingAppData}`           | C:\Users\Alice\AppData\Roaming           |
     user_config_path: VfsPath,
 
-    /// FIXME @alibektas : Change this to sth better.
     /// Config node whose values apply to **every** Rust project.
     user_config: Option<(GlobalLocalConfigInput, ConfigErrors)>,
 
-    /// A special file for this session whose path is set to `self.root_path.join("rust-analyzer.toml")`
-    root_ratoml_path: VfsPath,
-
-    /// This file can be used to make global changes while having only a workspace-wide scope.
-    root_ratoml: Option<(GlobalLocalConfigInput, ConfigErrors)>,
-
-    /// For every `SourceRoot` there can be at most one RATOML file.
-    ratoml_files: FxHashMap,
+    ratoml_file: FxHashMap,
 
     /// Clone of the value that is stored inside a `GlobalState`.
     source_root_parent_map: Arc>,
 
+    /// Use case : It is an error to have an empty value for `check_command`.
+    /// Since it is a `global` command at the moment, its final value can only be determined by
+    /// traversing through `global` configs and the `client` config. However the non-null value constraint
+    /// is config level agnostic, so this requires an independent error storage
+    validation_errors: ConfigErrors,
+
     detached_files: Vec,
 }
 
@@ -730,6 +832,7 @@ impl Config {
     /// The return tuple's bool component signals whether the `GlobalState` should call its `update_configuration()` method.
     fn apply_change_with_sink(&self, change: ConfigChange) -> (Config, bool) {
         let mut config = self.clone();
+        config.validation_errors = ConfigErrors::default();
 
         let mut should_update = false;
 
@@ -758,9 +861,10 @@ impl Config {
 
         if let Some(mut json) = change.client_config_change {
             tracing::info!("updating config from JSON: {:#}", json);
+
             if !(json.is_null() || json.as_object().map_or(false, |it| it.is_empty())) {
                 let mut json_errors = vec![];
-                let detached_files = get_field::>(
+                let detached_files = get_field_json::>(
                     &mut json,
                     &mut json_errors,
                     "detachedFiles",
@@ -773,6 +877,37 @@ impl Config {
 
                 patch_old_style::patch_json_for_outdated_configs(&mut json);
 
+                // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`.
+                config.snippets.clear();
+
+                let snips = self.completion_snippets_custom().to_owned();
+
+                for (name, def) in snips.iter() {
+                    if def.prefix.is_empty() && def.postfix.is_empty() {
+                        continue;
+                    }
+                    let scope = match def.scope {
+                        SnippetScopeDef::Expr => SnippetScope::Expr,
+                        SnippetScopeDef::Type => SnippetScope::Type,
+                        SnippetScopeDef::Item => SnippetScope::Item,
+                    };
+                    match Snippet::new(
+                        &def.prefix,
+                        &def.postfix,
+                        &def.body,
+                        def.description.as_ref().unwrap_or(name),
+                        &def.requires,
+                        scope,
+                    ) {
+                        Some(snippet) => config.snippets.push(snippet),
+                        None => json_errors.push((
+                            name.to_owned(),
+                            ::custom(format!(
+                                "snippet {name} is invalid or triggers are missing",
+                            )),
+                        )),
+                    }
+                }
                 config.client_config = (
                     FullConfigInput::from_json(json, &mut json_errors),
                     ConfigErrors(
@@ -788,68 +923,96 @@ impl Config {
             should_update = true;
         }
 
-        if let Some(change) = change.root_ratoml_change {
-            tracing::info!("updating root ra-toml config: {:#}", change);
-            #[allow(clippy::single_match)]
-            match toml::from_str(&change) {
-                Ok(table) => {
-                    let mut toml_errors = vec![];
-                    validate_toml_table(
-                        GlobalLocalConfigInput::FIELDS,
-                        &table,
-                        &mut String::new(),
-                        &mut toml_errors,
-                    );
-                    config.root_ratoml = Some((
-                        GlobalLocalConfigInput::from_toml(table, &mut toml_errors),
-                        ConfigErrors(
-                            toml_errors
-                                .into_iter()
-                                .map(|(a, b)| ConfigErrorInner::Toml { config_key: a, error: b })
-                                .map(Arc::new)
-                                .collect(),
-                        ),
-                    ));
-                    should_update = true;
-                }
-                // FIXME
-                Err(_) => (),
-            }
-        }
-
         if let Some(change) = change.ratoml_file_change {
-            for (source_root_id, (_, text)) in change {
-                if let Some(text) = text {
-                    let mut toml_errors = vec![];
-                    tracing::info!("updating ra-toml config: {:#}", text);
-                    #[allow(clippy::single_match)]
-                    match toml::from_str(&text) {
-                        Ok(table) => {
-                            validate_toml_table(
-                                &[LocalConfigInput::FIELDS],
-                                &table,
-                                &mut String::new(),
-                                &mut toml_errors,
-                            );
-                            config.ratoml_files.insert(
-                                source_root_id,
-                                (
-                                    LocalConfigInput::from_toml(&table, &mut toml_errors),
-                                    ConfigErrors(
-                                        toml_errors
-                                            .into_iter()
-                                            .map(|(a, b)| ConfigErrorInner::Toml {
-                                                config_key: a,
-                                                error: b,
-                                            })
-                                            .map(Arc::new)
-                                            .collect(),
-                                    ),
-                                ),
-                            );
+            for (source_root_id, (kind, _, text)) in change {
+                match kind {
+                    RatomlFileKind::Crate => {
+                        if let Some(text) = text {
+                            let mut toml_errors = vec![];
+                            tracing::info!("updating ra-toml config: {:#}", text);
+                            match toml::from_str(&text) {
+                                Ok(table) => {
+                                    validate_toml_table(
+                                        &[LocalConfigInput::FIELDS],
+                                        &table,
+                                        &mut String::new(),
+                                        &mut toml_errors,
+                                    );
+                                    config.ratoml_file.insert(
+                                        source_root_id,
+                                        (
+                                            RatomlFile::Crate(LocalConfigInput::from_toml(
+                                                &table,
+                                                &mut toml_errors,
+                                            )),
+                                            ConfigErrors(
+                                                toml_errors
+                                                    .into_iter()
+                                                    .map(|(a, b)| ConfigErrorInner::Toml {
+                                                        config_key: a,
+                                                        error: b,
+                                                    })
+                                                    .map(Arc::new)
+                                                    .collect(),
+                                            ),
+                                        ),
+                                    );
+                                }
+                                Err(e) => {
+                                    config.validation_errors.0.push(
+                                        ConfigErrorInner::ParseError {
+                                            reason: e.message().to_owned(),
+                                        }
+                                        .into(),
+                                    );
+                                }
+                            }
+                        }
+                    }
+                    RatomlFileKind::Workspace => {
+                        if let Some(text) = text {
+                            let mut toml_errors = vec![];
+                            match toml::from_str(&text) {
+                                Ok(table) => {
+                                    validate_toml_table(
+                                        GlobalLocalConfigInput::FIELDS,
+                                        &table,
+                                        &mut String::new(),
+                                        &mut toml_errors,
+                                    );
+                                    config.ratoml_file.insert(
+                                        source_root_id,
+                                        (
+                                            RatomlFile::Workspace(
+                                                GlobalLocalConfigInput::from_toml(
+                                                    table,
+                                                    &mut toml_errors,
+                                                ),
+                                            ),
+                                            ConfigErrors(
+                                                toml_errors
+                                                    .into_iter()
+                                                    .map(|(a, b)| ConfigErrorInner::Toml {
+                                                        config_key: a,
+                                                        error: b,
+                                                    })
+                                                    .map(Arc::new)
+                                                    .collect(),
+                                            ),
+                                        ),
+                                    );
+                                    should_update = true;
+                                }
+                                Err(e) => {
+                                    config.validation_errors.0.push(
+                                        ConfigErrorInner::ParseError {
+                                            reason: e.message().to_owned(),
+                                        }
+                                        .into(),
+                                    );
+                                }
+                            }
                         }
-                        // FIXME
-                        Err(_) => (),
                     }
                 }
             }
@@ -859,48 +1022,13 @@ impl Config {
             config.source_root_parent_map = source_root_map;
         }
 
-        // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`.
-        config.snippets.clear();
-
-        let snips = self.completion_snippets_custom().to_owned();
-
-        for (name, def) in snips.iter() {
-            if def.prefix.is_empty() && def.postfix.is_empty() {
-                continue;
-            }
-            let scope = match def.scope {
-                SnippetScopeDef::Expr => SnippetScope::Expr,
-                SnippetScopeDef::Type => SnippetScope::Type,
-                SnippetScopeDef::Item => SnippetScope::Item,
-            };
-            #[allow(clippy::single_match)]
-            match Snippet::new(
-                &def.prefix,
-                &def.postfix,
-                &def.body,
-                def.description.as_ref().unwrap_or(name),
-                &def.requires,
-                scope,
-            ) {
-                Some(snippet) => config.snippets.push(snippet),
-                // FIXME
-                // None => error_sink.0.push(ConfigErrorInner::Json {
-                //     config_key: "".to_owned(),
-                //     error: ::custom(format!(
-                //         "snippet {name} is invalid or triggers are missing",
-                //     )),
-                // }),
-                None => (),
-            }
+        if config.check_command(None).is_empty() {
+            config.validation_errors.0.push(Arc::new(ConfigErrorInner::Json {
+                config_key: "/check/command".to_owned(),
+                error: serde_json::Error::custom("expected a non-empty string"),
+            }));
         }
 
-        // FIXME: bring this back
-        // if config.check_command().is_empty() {
-        //     error_sink.0.push(ConfigErrorInner::Json {
-        //         config_key: "/check/command".to_owned(),
-        //         error: serde_json::Error::custom("expected a non-empty string"),
-        //     });
-        // }
         (config, should_update)
     }
 
@@ -915,22 +1043,37 @@ impl Config {
                 .1
                  .0
                 .iter()
-                .chain(config.root_ratoml.as_ref().into_iter().flat_map(|it| it.1 .0.iter()))
                 .chain(config.user_config.as_ref().into_iter().flat_map(|it| it.1 .0.iter()))
-                .chain(config.ratoml_files.values().flat_map(|it| it.1 .0.iter()))
+                .chain(config.ratoml_file.values().flat_map(|it| it.1 .0.iter()))
+                .chain(config.validation_errors.0.iter())
                 .cloned()
                 .collect(),
         );
         (config, e, should_update)
     }
+
+    pub fn add_linked_projects(&mut self, data: ProjectJsonData, buildfile: AbsPathBuf) {
+        let linked_projects = &mut self.client_config.0.global.linkedProjects;
+
+        let new_project = ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile };
+        match linked_projects {
+            Some(projects) => {
+                match projects.iter_mut().find(|p| p.manifest() == new_project.manifest()) {
+                    Some(p) => *p = new_project,
+                    None => projects.push(new_project),
+                }
+            }
+            None => *linked_projects = Some(vec![new_project]),
+        }
+    }
 }
 
 #[derive(Default, Debug)]
 pub struct ConfigChange {
     user_config_change: Option>,
-    root_ratoml_change: Option>,
     client_config_change: Option,
-    ratoml_file_change: Option>)>>,
+    ratoml_file_change:
+        Option>)>>,
     source_map_change: Option>>,
 }
 
@@ -940,10 +1083,10 @@ impl ConfigChange {
         source_root: SourceRootId,
         vfs_path: VfsPath,
         content: Option>,
-    ) -> Option<(VfsPath, Option>)> {
+    ) -> Option<(RatomlFileKind, VfsPath, Option>)> {
         self.ratoml_file_change
             .get_or_insert_with(Default::default)
-            .insert(source_root, (vfs_path, content))
+            .insert(source_root, (RatomlFileKind::Crate, vfs_path, content))
     }
 
     pub fn change_user_config(&mut self, content: Option>) {
@@ -951,9 +1094,15 @@ impl ConfigChange {
         self.user_config_change = content;
     }
 
-    pub fn change_root_ratoml(&mut self, content: Option>) {
-        assert!(self.root_ratoml_change.is_none()); // Otherwise it is a double write.
-        self.root_ratoml_change = content;
+    pub fn change_workspace_ratoml(
+        &mut self,
+        source_root: SourceRootId,
+        vfs_path: VfsPath,
+        content: Option>,
+    ) -> Option<(RatomlFileKind, VfsPath, Option>)> {
+        self.ratoml_file_change
+            .get_or_insert_with(Default::default)
+            .insert(source_root, (RatomlFileKind::Workspace, vfs_path, content))
     }
 
     pub fn change_client_config(&mut self, change: serde_json::Value) {
@@ -987,6 +1136,14 @@ impl From for LinkedProject {
     }
 }
 
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct DiscoverWorkspaceConfig {
+    pub command: Vec,
+    pub progress_label: String,
+    pub files_to_watch: Vec,
+}
+
 pub struct CallInfoConfig {
     pub params_only: bool,
     pub docs: bool,
@@ -1098,7 +1255,6 @@ pub enum FilesWatcher {
 #[derive(Debug, Clone)]
 pub struct NotificationsConfig {
     pub cargo_toml_not_found: bool,
-    pub unindexed_project: bool,
 }
 
 #[derive(Debug, Clone)]
@@ -1128,22 +1284,24 @@ pub struct WorkspaceSymbolConfig {
     /// How many items are returned at most.
     pub search_limit: usize,
 }
-
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct ClientCommandsConfig {
     pub run_single: bool,
     pub debug_single: bool,
     pub show_reference: bool,
     pub goto_location: bool,
     pub trigger_parameter_hints: bool,
+    pub rename: bool,
 }
 
 #[derive(Debug)]
 pub enum ConfigErrorInner {
     Json { config_key: String, error: serde_json::Error },
     Toml { config_key: String, error: toml::de::Error },
+    ParseError { reason: String },
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Default)]
 pub struct ConfigErrors(Vec>);
 
 impl ConfigErrors {
@@ -1165,6 +1323,7 @@ impl fmt::Display for ConfigErrors {
                 f(&": ")?;
                 f(e)
             }
+            ConfigErrorInner::ParseError { reason } => f(reason),
         });
         write!(f, "invalid config value{}:\n{}", if self.0.len() == 1 { "" } else { "s" }, errors)
     }
@@ -1196,11 +1355,6 @@ impl Config {
         // FIXME @alibektas : Temporary solution. I don't think this is right as at some point we may allow users to specify
         // custom USER_CONFIG_PATHs which may also be relative.
         let user_config_path = VfsPath::from(AbsPathBuf::assert(user_config_path));
-        let root_ratoml_path = {
-            let mut p = root_path.clone();
-            p.push("rust-analyzer.toml");
-            VfsPath::new_real_path(p.to_string())
-        };
 
         Config {
             caps: ClientCapabilities::new(caps),
@@ -1210,14 +1364,13 @@ impl Config {
             workspace_roots,
             visual_studio_code_version,
             client_config: (FullConfigInput::default(), ConfigErrors(vec![])),
-            ratoml_files: FxHashMap::default(),
             default_config: DEFAULT_CONFIG_DATA.get_or_init(|| Box::leak(Box::default())),
             source_root_parent_map: Arc::new(FxHashMap::default()),
             user_config: None,
             user_config_path,
-            root_ratoml: None,
-            root_ratoml_path,
             detached_files: Default::default(),
+            validation_errors: Default::default(),
+            ratoml_file: Default::default(),
         }
     }
 
@@ -1260,10 +1413,6 @@ impl Config {
         &self.root_path
     }
 
-    pub fn root_ratoml_path(&self) -> &VfsPath {
-        &self.root_ratoml_path
-    }
-
     pub fn caps(&self) -> &ClientCapabilities {
         &self.caps
     }
@@ -1317,11 +1466,11 @@ impl Config {
 
     pub fn diagnostics(&self, source_root: Option) -> DiagnosticsConfig {
         DiagnosticsConfig {
-            enabled: *self.diagnostics_enable(),
+            enabled: *self.diagnostics_enable(source_root),
             proc_attr_macros_enabled: self.expand_proc_attr_macros(),
             proc_macros_enabled: *self.procMacro_enable(),
-            disable_experimental: !self.diagnostics_experimental_enable(),
-            disabled: self.diagnostics_disabled().clone(),
+            disable_experimental: !self.diagnostics_experimental_enable(source_root),
+            disabled: self.diagnostics_disabled(source_root).clone(),
             expr_fill_default: match self.assist_expressionFillDefault(source_root) {
                 ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
                 ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
@@ -1331,7 +1480,7 @@ impl Config {
             prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
             prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
             prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
-            style_lints: self.diagnostics_styleLints_enable().to_owned(),
+            style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(),
             term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
             term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
         }
@@ -1524,22 +1673,34 @@ impl Config {
     }
 
     pub fn has_linked_projects(&self) -> bool {
-        !self.linkedProjects().is_empty()
+        !self.linkedProjects(None).is_empty()
     }
+
     pub fn linked_manifests(&self) -> impl Iterator + '_ {
-        self.linkedProjects().iter().filter_map(|it| match it {
+        self.linkedProjects(None).iter().filter_map(|it| match it {
             ManifestOrProjectJson::Manifest(p) => Some(&**p),
-            ManifestOrProjectJson::ProjectJson(_) => None,
+            // despite having a buildfile, using this variant as a manifest
+            // will fail.
+            ManifestOrProjectJson::DiscoveredProjectJson { .. } => None,
+            ManifestOrProjectJson::ProjectJson { .. } => None,
         })
     }
+
     pub fn has_linked_project_jsons(&self) -> bool {
-        self.linkedProjects().iter().any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_)))
+        self.linkedProjects(None)
+            .iter()
+            .any(|it| matches!(it, ManifestOrProjectJson::ProjectJson { .. }))
+    }
+
+    pub fn discover_workspace_config(&self) -> Option<&DiscoverWorkspaceConfig> {
+        self.workspace_discoverConfig(None).as_ref()
     }
+
     pub fn linked_or_discovered_projects(&self) -> Vec {
-        match self.linkedProjects().as_slice() {
+        match self.linkedProjects(None).as_slice() {
             [] => {
                 let exclude_dirs: Vec<_> =
-                    self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
+                    self.files_excludeDirs(None).iter().map(|p| self.root_path.join(p)).collect();
                 self.discovered_projects
                     .iter()
                     .filter(|project| {
@@ -1559,6 +1720,12 @@ impl Config {
                             .ok()
                             .map(Into::into)
                     }
+                    ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile } => {
+                        let root_path =
+                            buildfile.parent().expect("Unable to get parent of buildfile");
+
+                        Some(ProjectJson::new(None, root_path, data.clone()).into())
+                    }
                     ManifestOrProjectJson::ProjectJson(it) => {
                         Some(ProjectJson::new(None, &self.root_path, it.clone()).into())
                     }
@@ -1568,48 +1735,48 @@ impl Config {
     }
 
     pub fn prefill_caches(&self) -> bool {
-        self.cachePriming_enable().to_owned()
+        self.cachePriming_enable(None).to_owned()
     }
 
     pub fn publish_diagnostics(&self) -> bool {
-        self.diagnostics_enable().to_owned()
+        self.diagnostics_enable(None).to_owned()
     }
 
     pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
         DiagnosticsMapConfig {
-            remap_prefix: self.diagnostics_remapPrefix().clone(),
-            warnings_as_info: self.diagnostics_warningsAsInfo().clone(),
-            warnings_as_hint: self.diagnostics_warningsAsHint().clone(),
-            check_ignore: self.check_ignore().clone(),
+            remap_prefix: self.diagnostics_remapPrefix(None).clone(),
+            warnings_as_info: self.diagnostics_warningsAsInfo(None).clone(),
+            warnings_as_hint: self.diagnostics_warningsAsHint(None).clone(),
+            check_ignore: self.check_ignore(None).clone(),
         }
     }
 
     pub fn extra_args(&self) -> &Vec {
-        self.cargo_extraArgs()
+        self.cargo_extraArgs(None)
     }
 
     pub fn extra_env(&self) -> &FxHashMap {
-        self.cargo_extraEnv()
+        self.cargo_extraEnv(None)
     }
 
     pub fn check_extra_args(&self) -> Vec {
         let mut extra_args = self.extra_args().clone();
-        extra_args.extend_from_slice(self.check_extraArgs());
+        extra_args.extend_from_slice(self.check_extraArgs(None));
         extra_args
     }
 
     pub fn check_extra_env(&self) -> FxHashMap {
-        let mut extra_env = self.cargo_extraEnv().clone();
-        extra_env.extend(self.check_extraEnv().clone());
+        let mut extra_env = self.cargo_extraEnv(None).clone();
+        extra_env.extend(self.check_extraEnv(None).clone());
         extra_env
     }
 
-    pub fn lru_parse_query_capacity(&self) -> Option {
-        self.lru_capacity().to_owned()
+    pub fn lru_parse_query_capacity(&self) -> Option {
+        self.lru_capacity(None).to_owned()
     }
 
-    pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap, usize>> {
-        self.lru_query_capacities().is_empty().not().then(|| self.lru_query_capacities())
+    pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap, u16>> {
+        self.lru_query_capacities(None).is_empty().not().then(|| self.lru_query_capacities(None))
     }
 
     pub fn proc_macro_srv(&self) -> Option {
@@ -1618,7 +1785,7 @@ impl Config {
     }
 
     pub fn ignored_proc_macros(&self) -> &FxHashMap, Box<[Box]>> {
-        self.procMacro_ignored()
+        self.procMacro_ignored(None)
     }
 
     pub fn expand_proc_macros(&self) -> bool {
@@ -1633,34 +1800,37 @@ impl Config {
                 }
                 _ => FilesWatcher::Server,
             },
-            exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(),
+            exclude: self
+                .files_excludeDirs(None)
+                .iter()
+                .map(|it| self.root_path.join(it))
+                .collect(),
         }
     }
 
     pub fn notifications(&self) -> NotificationsConfig {
         NotificationsConfig {
             cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(),
-            unindexed_project: self.notifications_unindexedProject().to_owned(),
         }
     }
 
     pub fn cargo_autoreload_config(&self) -> bool {
-        self.cargo_autoreload().to_owned()
+        self.cargo_autoreload(None).to_owned()
     }
 
     pub fn run_build_scripts(&self) -> bool {
-        self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned()
+        self.cargo_buildScripts_enable(None).to_owned() || self.procMacro_enable().to_owned()
     }
 
     pub fn cargo(&self) -> CargoConfig {
-        let rustc_source = self.rustc_source().as_ref().map(|rustc_src| {
+        let rustc_source = self.rustc_source(None).as_ref().map(|rustc_src| {
             if rustc_src == "discover" {
                 RustLibSource::Discover
             } else {
                 RustLibSource::Path(self.root_path.join(rustc_src))
             }
         });
-        let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| {
+        let sysroot = self.cargo_sysroot(None).as_ref().map(|sysroot| {
             if sysroot == "discover" {
                 RustLibSource::Discover
             } else {
@@ -1668,30 +1838,33 @@ impl Config {
             }
         });
         let sysroot_src =
-            self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot));
-        let sysroot_query_metadata = self.cargo_sysrootQueryMetadata();
+            self.cargo_sysrootSrc(None).as_ref().map(|sysroot| self.root_path.join(sysroot));
+        let sysroot_query_metadata = self.cargo_sysrootQueryMetadata(None);
 
         CargoConfig {
-            all_targets: *self.cargo_allTargets(),
-            features: match &self.cargo_features() {
+            all_targets: *self.cargo_allTargets(None),
+            features: match &self.cargo_features(None) {
                 CargoFeaturesDef::All => CargoFeatures::All,
                 CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
                     features: features.clone(),
-                    no_default_features: self.cargo_noDefaultFeatures().to_owned(),
+                    no_default_features: self.cargo_noDefaultFeatures(None).to_owned(),
                 },
             },
-            target: self.cargo_target().clone(),
+            target: self.cargo_target(None).clone(),
             sysroot,
             sysroot_query_metadata: *sysroot_query_metadata,
             sysroot_src,
             rustc_source,
             cfg_overrides: project_model::CfgOverrides {
                 global: CfgDiff::new(
-                    self.cargo_cfgs()
+                    self.cargo_cfgs(None)
                         .iter()
                         .map(|(key, val)| match val {
-                            Some(val) => CfgAtom::KeyValue { key: key.into(), value: val.into() },
-                            None => CfgAtom::Flag(key.into()),
+                            Some(val) => CfgAtom::KeyValue {
+                                key: Symbol::intern(key),
+                                value: Symbol::intern(val),
+                            },
+                            None => CfgAtom::Flag(Symbol::intern(key)),
                         })
                         .collect(),
                     vec![],
@@ -1699,49 +1872,49 @@ impl Config {
                 .unwrap(),
                 selective: Default::default(),
             },
-            wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(),
-            invocation_strategy: match self.cargo_buildScripts_invocationStrategy() {
+            wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(None),
+            invocation_strategy: match self.cargo_buildScripts_invocationStrategy(None) {
                 InvocationStrategy::Once => project_model::InvocationStrategy::Once,
                 InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
             },
-            invocation_location: match self.cargo_buildScripts_invocationLocation() {
+            invocation_location: match self.cargo_buildScripts_invocationLocation(None) {
                 InvocationLocation::Root => {
                     project_model::InvocationLocation::Root(self.root_path.clone())
                 }
                 InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
             },
-            run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(),
-            extra_args: self.cargo_extraArgs().clone(),
-            extra_env: self.cargo_extraEnv().clone(),
+            run_build_script_command: self.cargo_buildScripts_overrideCommand(None).clone(),
+            extra_args: self.cargo_extraArgs(None).clone(),
+            extra_env: self.cargo_extraEnv(None).clone(),
             target_dir: self.target_dir_from_config(),
         }
     }
 
-    pub fn rustfmt(&self) -> RustfmtConfig {
-        match &self.rustfmt_overrideCommand() {
+    pub fn rustfmt(&self, source_root_id: Option) -> RustfmtConfig {
+        match &self.rustfmt_overrideCommand(source_root_id) {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
                 RustfmtConfig::CustomCommand { command, args }
             }
             Some(_) | None => RustfmtConfig::Rustfmt {
-                extra_args: self.rustfmt_extraArgs().clone(),
-                enable_range_formatting: *self.rustfmt_rangeFormatting_enable(),
+                extra_args: self.rustfmt_extraArgs(source_root_id).clone(),
+                enable_range_formatting: *self.rustfmt_rangeFormatting_enable(source_root_id),
             },
         }
     }
 
     pub fn flycheck_workspace(&self) -> bool {
-        *self.check_workspace()
+        *self.check_workspace(None)
     }
 
     pub fn cargo_test_options(&self) -> CargoOptions {
         CargoOptions {
-            target_triples: self.cargo_target().clone().into_iter().collect(),
+            target_triples: self.cargo_target(None).clone().into_iter().collect(),
             all_targets: false,
-            no_default_features: *self.cargo_noDefaultFeatures(),
-            all_features: matches!(self.cargo_features(), CargoFeaturesDef::All),
-            features: match self.cargo_features().clone() {
+            no_default_features: *self.cargo_noDefaultFeatures(None),
+            all_features: matches!(self.cargo_features(None), CargoFeaturesDef::All),
+            features: match self.cargo_features(None).clone() {
                 CargoFeaturesDef::All => vec![],
                 CargoFeaturesDef::Selected(it) => it,
             },
@@ -1752,7 +1925,7 @@ impl Config {
     }
 
     pub fn flycheck(&self) -> FlycheckConfig {
-        match &self.check_overrideCommand() {
+        match &self.check_overrideCommand(None) {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
@@ -1760,13 +1933,13 @@ impl Config {
                     command,
                     args,
                     extra_env: self.check_extra_env(),
-                    invocation_strategy: match self.check_invocationStrategy() {
+                    invocation_strategy: match self.check_invocationStrategy(None) {
                         InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
                         InvocationStrategy::PerWorkspace => {
                             flycheck::InvocationStrategy::PerWorkspace
                         }
                     },
-                    invocation_location: match self.check_invocationLocation() {
+                    invocation_location: match self.check_invocationLocation(None) {
                         InvocationLocation::Root => {
                             flycheck::InvocationLocation::Root(self.root_path.clone())
                         }
@@ -1775,28 +1948,30 @@ impl Config {
                 }
             }
             Some(_) | None => FlycheckConfig::CargoCommand {
-                command: self.check_command().clone(),
+                command: self.check_command(None).clone(),
                 options: CargoOptions {
                     target_triples: self
-                        .check_targets()
+                        .check_targets(None)
                         .clone()
                         .and_then(|targets| match &targets.0[..] {
                             [] => None,
                             targets => Some(targets.into()),
                         })
-                        .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()),
-                    all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()),
+                        .unwrap_or_else(|| self.cargo_target(None).clone().into_iter().collect()),
+                    all_targets: self
+                        .check_allTargets(None)
+                        .unwrap_or(*self.cargo_allTargets(None)),
                     no_default_features: self
-                        .check_noDefaultFeatures()
-                        .unwrap_or(*self.cargo_noDefaultFeatures()),
+                        .check_noDefaultFeatures(None)
+                        .unwrap_or(*self.cargo_noDefaultFeatures(None)),
                     all_features: matches!(
-                        self.check_features().as_ref().unwrap_or(self.cargo_features()),
+                        self.check_features(None).as_ref().unwrap_or(self.cargo_features(None)),
                         CargoFeaturesDef::All
                     ),
                     features: match self
-                        .check_features()
+                        .check_features(None)
                         .clone()
-                        .unwrap_or_else(|| self.cargo_features().clone())
+                        .unwrap_or_else(|| self.cargo_features(None).clone())
                     {
                         CargoFeaturesDef::All => vec![],
                         CargoFeaturesDef::Selected(it) => it,
@@ -1811,7 +1986,7 @@ impl Config {
     }
 
     fn target_dir_from_config(&self) -> Option {
-        self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir {
+        self.cargo_targetDir(None).as_ref().and_then(|target_dir| match target_dir {
             TargetDirectory::UseSubdirectory(true) => {
                 Some(Utf8PathBuf::from("target/rust-analyzer"))
             }
@@ -1822,18 +1997,18 @@ impl Config {
     }
 
     pub fn check_on_save(&self) -> bool {
-        *self.checkOnSave()
+        *self.checkOnSave(None)
     }
 
     pub fn script_rebuild_on_save(&self) -> bool {
-        *self.cargo_buildScripts_rebuildOnSave()
+        *self.cargo_buildScripts_rebuildOnSave(None)
     }
 
     pub fn runnables(&self) -> RunnablesConfig {
         RunnablesConfig {
-            override_cargo: self.runnables_command().clone(),
-            cargo_extra_args: self.runnables_extraArgs().clone(),
-            extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(),
+            override_cargo: self.runnables_command(None).clone(),
+            cargo_extra_args: self.runnables_extraArgs(None).clone(),
+            extra_test_binary_args: self.runnables_extraTestBinaryArgs(None).clone(),
         }
     }
 
@@ -1889,23 +2064,22 @@ impl Config {
     }
 
     pub fn client_commands(&self) -> ClientCommandsConfig {
-        let commands = self.commands();
-        let force = commands.is_none() && *self.lens_forceCustomCommands();
-        let commands = commands.map(|it| it.commands).unwrap_or_default();
+        let commands = self.commands().map(|it| it.commands).unwrap_or_default();
 
-        let get = |name: &str| commands.iter().any(|it| it == name) || force;
+        let get = |name: &str| commands.iter().any(|it| it == name);
 
         ClientCommandsConfig {
             run_single: get("rust-analyzer.runSingle"),
             debug_single: get("rust-analyzer.debugSingle"),
             show_reference: get("rust-analyzer.showReferences"),
             goto_location: get("rust-analyzer.gotoLocation"),
-            trigger_parameter_hints: get("editor.action.triggerParameterHints"),
+            trigger_parameter_hints: get("rust-analyzer.triggerParameterHints"),
+            rename: get("rust-analyzer.rename"),
         }
     }
 
     pub fn prime_caches_num_threads(&self) -> usize {
-        match self.cachePriming_numThreads() {
+        match self.cachePriming_numThreads(None) {
             NumThreads::Concrete(0) | NumThreads::Physical => num_cpus::get_physical(),
             &NumThreads::Concrete(n) => n,
             NumThreads::Logical => num_cpus::get(),
@@ -2095,11 +2269,47 @@ mod single_or_array {
     }
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
 #[serde(untagged)]
 enum ManifestOrProjectJson {
     Manifest(Utf8PathBuf),
     ProjectJson(ProjectJsonData),
+    DiscoveredProjectJson {
+        data: ProjectJsonData,
+        #[serde(serialize_with = "serialize_abs_pathbuf")]
+        #[serde(deserialize_with = "deserialize_abs_pathbuf")]
+        buildfile: AbsPathBuf,
+    },
+}
+
+fn deserialize_abs_pathbuf<'de, D>(de: D) -> std::result::Result
+where
+    D: serde::de::Deserializer<'de>,
+{
+    let path = String::deserialize(de)?;
+
+    AbsPathBuf::try_from(path.as_ref())
+        .map_err(|err| serde::de::Error::custom(format!("invalid path name: {err:?}")))
+}
+
+fn serialize_abs_pathbuf(path: &AbsPathBuf, se: S) -> Result
+where
+    S: serde::Serializer,
+{
+    let path: &Utf8Path = path.as_ref();
+    se.serialize_str(path.as_str())
+}
+
+impl ManifestOrProjectJson {
+    fn manifest(&self) -> Option<&Utf8Path> {
+        match self {
+            ManifestOrProjectJson::Manifest(manifest) => Some(manifest),
+            ManifestOrProjectJson::DiscoveredProjectJson { buildfile, .. } => {
+                Some(buildfile.as_ref())
+            }
+            ManifestOrProjectJson::ProjectJson(_) => None,
+        }
+    }
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -2341,20 +2551,23 @@ macro_rules! _impl_for_config_data {
                 $($doc)*
                 #[allow(non_snake_case)]
                 $vis fn $field(&self, source_root: Option) -> &$ty {
-                    let mut par: Option = source_root;
-                    while let Some(source_root_id) = par {
-                        par = self.source_root_parent_map.get(&source_root_id).copied();
-                        if let Some((config, _)) = self.ratoml_files.get(&source_root_id) {
-                            if let Some(value) = config.$field.as_ref() {
-                                return value;
+                    let mut source_root = source_root.as_ref();
+                    while let Some(sr) = source_root {
+                        if let Some((file, _)) = self.ratoml_file.get(&sr) {
+                            match file {
+                                RatomlFile::Workspace(config) => {
+                                    if let Some(v) = config.local.$field.as_ref() {
+                                        return &v;
+                                    }
+                                },
+                                RatomlFile::Crate(config) => {
+                                    if let Some(value) = config.$field.as_ref() {
+                                        return value;
+                                    }
+                                }
                             }
                         }
-                    }
-
-                    if let Some((root_path_ratoml, _)) = self.root_ratoml.as_ref() {
-                        if let Some(v) = root_path_ratoml.local.$field.as_ref() {
-                            return &v;
-                        }
+                        source_root = self.source_root_parent_map.get(&sr);
                     }
 
                     if let Some(v) = self.client_config.0.local.$field.as_ref() {
@@ -2381,12 +2594,16 @@ macro_rules! _impl_for_config_data {
             $(
                 $($doc)*
                 #[allow(non_snake_case)]
-                $vis fn $field(&self) -> &$ty {
-
-                    if let Some((root_path_ratoml, _)) = self.root_ratoml.as_ref() {
-                        if let Some(v) = root_path_ratoml.global.$field.as_ref() {
-                            return &v;
+                $vis fn $field(&self, source_root : Option) -> &$ty {
+                    let mut source_root = source_root.as_ref();
+                    while let Some(sr) = source_root {
+                        if let Some((RatomlFile::Workspace(config), _)) = self.ratoml_file.get(&sr) {
+                            if let Some(v) = config.global.$field.as_ref() {
+                                return &v;
+                            }
                         }
+
+                        source_root = self.source_root_parent_map.get(&sr);
                     }
 
                     if let Some(v) = self.client_config.0.global.$field.as_ref() {
@@ -2399,6 +2616,7 @@ macro_rules! _impl_for_config_data {
                         }
                     }
 
+
                     &self.default_config.global.$field
                 }
             )*
@@ -2479,7 +2697,7 @@ macro_rules! _config_data {
 
             fn from_json(json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> Self {
                 Self {$(
-                    $field: get_field(
+                    $field: get_field_json(
                         json,
                         error_sink,
                         stringify!($field),
@@ -2597,7 +2815,7 @@ impl GlobalLocalConfigInput {
     }
 }
 
-fn get_field(
+fn get_field_json(
     json: &mut serde_json::Value,
     error_sink: &mut Vec<(String, serde_json::Error)>,
     field: &'static str,
@@ -2745,7 +2963,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
         "FxHashMap" => set! {
             "type": "object",
         },
-        "FxHashMap, usize>" => set! {
+        "FxHashMap, u16>" => set! {
             "type": "object",
         },
         "FxHashMap>" => set! {
@@ -2755,6 +2973,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
             "type": ["null", "integer"],
             "minimum": 0,
         },
+        "Option" => set! {
+            "type": ["null", "integer"],
+            "minimum": 0,
+            "maximum": 65535,
+        },
         "Option" => set! {
             "type": ["null", "string"],
         },
@@ -3078,6 +3301,29 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 },
             ],
         },
+        "Option" => set! {
+            "anyOf": [
+                {
+                    "type": "null"
+                },
+                {
+                    "type": "object",
+                    "properties": {
+                        "command": {
+                            "type": "array",
+                            "items": { "type": "string" }
+                        },
+                        "progressLabel": {
+                            "type": "string"
+                        },
+                        "filesToWatch": {
+                            "type": "array",
+                            "items": { "type": "string" }
+                        },
+                    }
+                }
+            ]
+        },
         _ => panic!("missing entry for {ty}: {default} (field {field})"),
     }
 
@@ -3100,7 +3346,7 @@ fn validate_toml_table(
         ptr.push_str(k);
 
         match v {
-            // This is a table config, any entry in it is therefor valid
+            // This is a table config, any entry in it is therefore valid
             toml::Value::Table(_) if verify(ptr) => (),
             toml::Value::Table(table) => validate_toml_table(known_ptrs, table, ptr, error_sink),
             _ if !verify(ptr) => error_sink
@@ -3300,7 +3546,7 @@ mod tests {
         }));
 
         (config, _, _) = config.apply_change(change);
-        assert_eq!(config.cargo_targetDir(), &None);
+        assert_eq!(config.cargo_targetDir(None), &None);
         assert!(
             matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
         );
@@ -3323,7 +3569,7 @@ mod tests {
 
         (config, _, _) = config.apply_change(change);
 
-        assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true)));
+        assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true)));
         assert!(
             matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
         );
@@ -3347,7 +3593,7 @@ mod tests {
         (config, _, _) = config.apply_change(change);
 
         assert_eq!(
-            config.cargo_targetDir(),
+            config.cargo_targetDir(None),
             &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
         );
         assert!(
@@ -3367,7 +3613,7 @@ mod tests {
 
         let mut change = ConfigChange::default();
 
-        change.change_root_ratoml(Some(
+        change.change_user_config(Some(
             toml::toml! {
                 [cargo.cfgs]
                 these = "these"
@@ -3426,21 +3672,7 @@ mod tests {
         let (_, e, _) = config.apply_change(change);
         expect_test::expect![[r#"
             ConfigErrors(
-                [
-                    Toml {
-                        config_key: "invalid/config/err",
-                        error: Error {
-                            inner: Error {
-                                inner: TomlError {
-                                    message: "unexpected field",
-                                    raw: None,
-                                    keys: [],
-                                    span: None,
-                                },
-                            },
-                        },
-                    },
-                ],
+                [],
             )
         "#]]
         .assert_debug_eq(&e);
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 de4c9586dfd74..f1dde104fce4a 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
@@ -6,10 +6,11 @@
 use std::{ops::Not as _, time::Instant};
 
 use crossbeam_channel::{unbounded, Receiver, Sender};
-use flycheck::FlycheckHandle;
+use flycheck::{project_json, FlycheckHandle};
 use hir::ChangeWithProcMacros;
 use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
 use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabaseExt};
+use itertools::Itertools;
 use load_cargo::SourceRootConfig;
 use lsp_types::{SemanticTokens, Url};
 use nohash_hasher::IntMap;
@@ -20,18 +21,15 @@ use parking_lot::{
 use proc_macro_api::ProcMacroServer;
 use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
 use rustc_hash::{FxHashMap, FxHashSet};
-use tracing::{span, Level};
+use tracing::{span, trace, Level};
 use triomphe::Arc;
-use vfs::{AnchoredPathBuf, ChangeKind, Vfs};
+use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath};
 
 use crate::{
-    config::{Config, ConfigChange, ConfigErrors},
+    config::{Config, ConfigChange, ConfigErrors, RatomlFileKind},
     diagnostics::{CheckFixes, DiagnosticCollection},
     line_index::{LineEndings, LineIndex},
-    lsp::{
-        from_proto::{self},
-        to_proto::url_from_abs_path,
-    },
+    lsp::{from_proto, to_proto::url_from_abs_path},
     lsp_ext,
     main_loop::Task,
     mem_docs::MemDocs,
@@ -41,6 +39,11 @@ use crate::{
     task_pool::{TaskPool, TaskQueue},
 };
 
+pub(crate) struct FetchWorkspaceRequest {
+    pub(crate) path: Option,
+    pub(crate) force_crate_graph_reload: bool,
+}
+
 // Enforces drop order
 pub(crate) struct Handle {
     pub(crate) handle: H,
@@ -95,6 +98,11 @@ pub(crate) struct GlobalState {
     pub(crate) test_run_receiver: Receiver,
     pub(crate) test_run_remaining_jobs: usize,
 
+    // Project loading
+    pub(crate) discover_handle: Option,
+    pub(crate) discover_sender: Sender,
+    pub(crate) discover_receiver: Receiver,
+
     // VFS
     pub(crate) loader: Handle, Receiver>,
     pub(crate) vfs: Arc)>>,
@@ -134,11 +142,12 @@ pub(crate) struct GlobalState {
 
     // op queues
     pub(crate) fetch_workspaces_queue:
-        OpQueue>, bool)>>,
+        OpQueue>, bool)>>,
     pub(crate) fetch_build_data_queue:
         OpQueue<(), (Arc>, Vec>)>,
     pub(crate) fetch_proc_macros_queue: OpQueue, bool>,
     pub(crate) prime_caches_queue: OpQueue,
+    pub(crate) discover_workspace_queue: OpQueue,
 
     /// A deferred task queue.
     ///
@@ -146,7 +155,7 @@ pub(crate) struct GlobalState {
     /// handlers, as accessing the database may block latency-sensitive
     /// interactions and should be moved away from the main thread.
     ///
-    /// For certain features, such as [`lsp_ext::UnindexedProjectParams`],
+    /// For certain features, such as [`GlobalState::handle_discover_msg`],
     /// this queue should run only *after* [`GlobalState::process_changes`] has
     /// been called.
     pub(crate) deferred_task_queue: TaskQueue,
@@ -202,6 +211,9 @@ impl GlobalState {
         }
         let (flycheck_sender, flycheck_receiver) = unbounded();
         let (test_run_sender, test_run_receiver) = unbounded();
+
+        let (discover_sender, discover_receiver) = unbounded();
+
         let mut this = GlobalState {
             sender,
             req_queue: ReqQueue::default(),
@@ -233,6 +245,10 @@ impl GlobalState {
             test_run_receiver,
             test_run_remaining_jobs: 0,
 
+            discover_handle: None,
+            discover_sender,
+            discover_receiver,
+
             vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
             vfs_config_version: 0,
             vfs_progress_config_version: 0,
@@ -247,6 +263,7 @@ impl GlobalState {
             fetch_proc_macros_queue: OpQueue::default(),
 
             prime_caches_queue: OpQueue::default(),
+            discover_workspace_queue: OpQueue::default(),
 
             deferred_task_queue: task_queue,
         };
@@ -296,11 +313,24 @@ impl GlobalState {
                         modified_rust_files.push(file.file_id);
                     }
 
+                    let additional_files = self
+                        .config
+                        .discover_workspace_config()
+                        .map(|cfg| {
+                            cfg.files_to_watch.iter().map(String::as_str).collect::>()
+                        })
+                        .unwrap_or_default();
+
                     let path = path.to_path_buf();
                     if file.is_created_or_deleted() {
                         workspace_structure_change.get_or_insert((path, false)).1 |=
                             self.crate_graph_file_dependencies.contains(vfs_path);
-                    } else if reload::should_refresh_for_change(&path, file.kind()) {
+                    } else if reload::should_refresh_for_change(
+                        &path,
+                        file.kind(),
+                        &additional_files,
+                    ) {
+                        trace!(?path, kind = ?file.kind(), "refreshing for a change");
                         workspace_structure_change.get_or_insert((path.clone(), false));
                     }
                 }
@@ -350,37 +380,62 @@ impl GlobalState {
         {
             let config_change = {
                 let user_config_path = self.config.user_config_path();
-                let root_ratoml_path = self.config.root_ratoml_path();
                 let mut change = ConfigChange::default();
                 let db = self.analysis_host.raw_database();
 
+                // FIXME @alibektas : This is silly. There is no reason to use VfsPaths when there is SourceRoots. But how
+                // do I resolve a "workspace_root" to its corresponding id without having to rely on a cargo.toml's ( or project json etc.) file id?
+                let workspace_ratoml_paths = self
+                    .workspaces
+                    .iter()
+                    .map(|ws| {
+                        VfsPath::from({
+                            let mut p = ws.workspace_root().to_owned();
+                            p.push("rust-analyzer.toml");
+                            p
+                        })
+                    })
+                    .collect_vec();
+
                 for (file_id, (_change_kind, vfs_path)) in modified_ratoml_files {
                     if vfs_path == *user_config_path {
                         change.change_user_config(Some(db.file_text(file_id)));
                         continue;
                     }
 
-                    if vfs_path == *root_ratoml_path {
-                        change.change_root_ratoml(Some(db.file_text(file_id)));
-                        continue;
-                    }
-
                     // If change has been made to a ratoml file that
                     // belongs to a non-local source root, we will ignore it.
-                    // As it doesn't make sense a users to use external config files.
                     let sr_id = db.file_source_root(file_id);
                     let sr = db.source_root(sr_id);
+
                     if !sr.is_library {
-                        if let Some((old_path, old_text)) = change.change_ratoml(
-                            sr_id,
-                            vfs_path.clone(),
-                            Some(db.file_text(file_id)),
-                        ) {
+                        let entry = if workspace_ratoml_paths.contains(&vfs_path) {
+                            change.change_workspace_ratoml(
+                                sr_id,
+                                vfs_path.clone(),
+                                Some(db.file_text(file_id)),
+                            )
+                        } else {
+                            change.change_ratoml(
+                                sr_id,
+                                vfs_path.clone(),
+                                Some(db.file_text(file_id)),
+                            )
+                        };
+
+                        if let Some((kind, old_path, old_text)) = entry {
                             // SourceRoot has more than 1 RATOML files. In this case lexicographically smaller wins.
                             if old_path < vfs_path {
                                 span!(Level::ERROR, "Two `rust-analyzer.toml` files were found inside the same crate. {vfs_path} has no effect.");
                                 // Put the old one back in.
-                                change.change_ratoml(sr_id, old_path, old_text);
+                                match kind {
+                                    RatomlFileKind::Crate => {
+                                        change.change_ratoml(sr_id, old_path, old_text);
+                                    }
+                                    RatomlFileKind::Workspace => {
+                                        change.change_workspace_ratoml(sr_id, old_path, old_text);
+                                    }
+                                }
                             }
                         }
                     } else {
@@ -398,7 +453,7 @@ impl GlobalState {
             if should_update {
                 self.update_configuration(config);
             } else {
-                // No global or client level config was changed. So we can just naively replace config.
+                // No global or client level config was changed. So we can naively replace config.
                 self.config = Arc::new(config);
             }
         }
@@ -419,7 +474,7 @@ impl GlobalState {
 
                 self.fetch_workspaces_queue.request_op(
                     format!("workspace vfs file change: {path}"),
-                    force_crate_graph_reload,
+                    FetchWorkspaceRequest { path: Some(path.to_owned()), force_crate_graph_reload },
                 );
             }
         }
@@ -579,6 +634,7 @@ impl GlobalStateSnapshot {
                         target_kind: target_data.kind,
                         required_features: target_data.required_features.clone(),
                         features: package_data.features.keys().cloned().collect(),
+                        sysroot_root: workspace.sysroot.root().map(ToOwned::to_owned),
                     }));
                 }
                 ProjectWorkspaceKind::Json(project) => {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 095d7c941c104..4b14dcfc3721b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -14,7 +14,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath};
 
 use crate::{
     config::{Config, ConfigChange},
-    global_state::GlobalState,
+    global_state::{FetchWorkspaceRequest, GlobalState},
     lsp::{from_proto, utils::apply_document_changes},
     lsp_ext::{self, RunFlycheckParams},
     mem_docs::DocumentData,
@@ -73,7 +73,7 @@ pub(crate) fn handle_did_open_text_document(
 
         tracing::info!("New file content set {:?}", params.text_document.text);
         state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes()));
-        if state.config.notifications().unindexed_project {
+        if state.config.discover_workspace_config().is_some() {
             tracing::debug!("queuing task");
             let _ = state
                 .deferred_task_queue
@@ -150,15 +150,29 @@ pub(crate) fn handle_did_save_text_document(
 
     if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) {
         // Re-fetch workspaces if a workspace related file has changed
-        if let Some(abs_path) = vfs_path.as_path() {
-            if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
-                state
-                    .fetch_workspaces_queue
-                    .request_op(format!("workspace vfs file change saved {abs_path}"), false);
-            } else if state.detached_files.contains(abs_path) {
-                state
-                    .fetch_workspaces_queue
-                    .request_op(format!("detached file saved {abs_path}"), false);
+        if let Some(path) = vfs_path.as_path() {
+            let additional_files = &state
+                .config
+                .discover_workspace_config()
+                .map(|cfg| cfg.files_to_watch.iter().map(String::as_str).collect::>())
+                .unwrap_or_default();
+
+            if reload::should_refresh_for_change(path, ChangeKind::Modify, additional_files) {
+                state.fetch_workspaces_queue.request_op(
+                    format!("workspace vfs file change saved {path}"),
+                    FetchWorkspaceRequest {
+                        path: Some(path.to_owned()),
+                        force_crate_graph_reload: false,
+                    },
+                );
+            } else if state.detached_files.contains(path) {
+                state.fetch_workspaces_queue.request_op(
+                    format!("detached file saved {path}"),
+                    FetchWorkspaceRequest {
+                        path: Some(path.to_owned()),
+                        force_crate_graph_reload: false,
+                    },
+                );
             }
         }
 
@@ -240,7 +254,9 @@ pub(crate) fn handle_did_change_workspace_folders(
 
     if !config.has_linked_projects() && config.detached_files().is_empty() {
         config.rediscover_workspaces();
-        state.fetch_workspaces_queue.request_op("client workspaces changed".to_owned(), false)
+
+        let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false };
+        state.fetch_workspaces_queue.request_op("client workspaces changed".to_owned(), req);
     }
 
     Ok(())
@@ -274,7 +290,6 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                 .into_iter()
                 .flat_map(|id| world.analysis.transitive_rev_deps(id))
                 .flatten()
-                .sorted()
                 .unique()
                 .collect();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index e19f7a4898b93..eca139d79ae6a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -27,7 +27,7 @@ use lsp_types::{
     SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
 };
 use paths::Utf8PathBuf;
-use project_model::{ManifestPath, ProjectWorkspaceKind, TargetKind};
+use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind};
 use serde_json::json;
 use stdx::{format_to, never};
 use syntax::{algo, ast, AstNode, TextRange, TextSize};
@@ -37,7 +37,7 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
 use crate::{
     config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
     diff::diff,
-    global_state::{GlobalState, GlobalStateSnapshot},
+    global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
     hack_recover_crate_name,
     line_index::LineEndings,
     lsp::{
@@ -50,14 +50,15 @@ use crate::{
         self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams,
         FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
     },
-    target_spec::TargetSpec,
+    target_spec::{CargoTargetSpec, TargetSpec},
 };
 
 pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
     state.proc_macro_clients = Arc::from_iter([]);
     state.build_deps_changed = false;
 
-    state.fetch_workspaces_queue.request_op("reload workspace request".to_owned(), false);
+    let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false };
+    state.fetch_workspaces_queue.request_op("reload workspace request".to_owned(), req);
     Ok(())
 }
 
@@ -110,6 +111,13 @@ pub(crate) fn handle_analyzer_status(
             .status(file_id)
             .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
     );
+
+    buf.push_str("\nVersion: \n");
+    format_to!(buf, "{}", crate::version());
+
+    buf.push_str("\nConfiguration: \n");
+    format_to!(buf, "{:?}", snap.config);
+
     Ok(buf)
 }
 
@@ -126,11 +134,6 @@ pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Res
     Ok(out)
 }
 
-pub(crate) fn handle_shuffle_crate_graph(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
-    state.analysis_host.shuffle_crate_graph();
-    Ok(())
-}
-
 pub(crate) fn handle_syntax_tree(
     snap: GlobalStateSnapshot,
     params: lsp_ext::SyntaxTreeParams,
@@ -191,6 +194,20 @@ pub(crate) fn handle_view_item_tree(
     Ok(res)
 }
 
+// cargo test requires the real package name which might contain hyphens but
+// the test identifier passed to this function is the namespace form where hyphens
+// are replaced with underscores so we have to reverse this and find the real package name
+fn find_package_name(namespace_root: &str, cargo: &CargoWorkspace) -> Option {
+    cargo.packages().find_map(|p| {
+        let package_name = &cargo[p].name;
+        if package_name.replace('-', "_") == namespace_root {
+            Some(package_name.clone())
+        } else {
+            None
+        }
+    })
+}
+
 pub(crate) fn handle_run_test(
     state: &mut GlobalState,
     params: lsp_ext::RunTestParams,
@@ -198,7 +215,7 @@ pub(crate) fn handle_run_test(
     if let Some(_session) = state.test_run_session.take() {
         state.send_notification::(());
     }
-    // We detect the lowest common ansector of all included tests, and
+    // We detect the lowest common ancestor of all included tests, and
     // run it. We ignore excluded tests for now, the client will handle
     // it for us.
     let lca = match params.include {
@@ -217,20 +234,31 @@ pub(crate) fn handle_run_test(
             .unwrap_or_default(),
         None => "".to_owned(),
     };
-    let test_path = if lca.is_empty() {
-        None
-    } else if let Some((_, path)) = lca.split_once("::") {
-        Some(path)
+    let (namespace_root, test_path) = if lca.is_empty() {
+        (None, None)
+    } else if let Some((namespace_root, path)) = lca.split_once("::") {
+        (Some(namespace_root), Some(path))
     } else {
-        None
+        (Some(lca.as_str()), None)
     };
     let mut handles = vec![];
     for ws in &*state.workspaces {
         if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind {
+            let test_target = if let Some(namespace_root) = namespace_root {
+                if let Some(package_name) = find_package_name(namespace_root, cargo) {
+                    flycheck::TestTarget::Package(package_name)
+                } else {
+                    flycheck::TestTarget::Workspace
+                }
+            } else {
+                flycheck::TestTarget::Workspace
+            };
+
             let handle = flycheck::CargoTestHandle::new(
                 test_path,
                 state.config.cargo_test_options(),
                 cargo.workspace_root(),
+                test_target,
                 state.test_run_sender.clone(),
             )?;
             handles.push(handle);
@@ -848,6 +876,14 @@ pub(crate) fn handle_runnables(
                 if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args {
                     runnable.label = format!("{} + expect", runnable.label);
                     r.environment.insert("UPDATE_EXPECT".to_owned(), "1".to_owned());
+                    if let Some(TargetSpec::Cargo(CargoTargetSpec {
+                        sysroot_root: Some(sysroot_root),
+                        ..
+                    })) = &target_spec
+                    {
+                        r.environment
+                            .insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string());
+                    }
                 }
             }
             res.push(runnable);
@@ -889,7 +925,12 @@ pub(crate) fn handle_runnables(
                         override_cargo: config.override_cargo.clone(),
                         cargo_args,
                         executable_args: Vec::new(),
-                        environment: Default::default(),
+                        environment: spec
+                            .sysroot_root
+                            .as_ref()
+                            .map(|root| ("RUSTC_TOOLCHAIN".to_owned(), root.to_string()))
+                            .into_iter()
+                            .collect(),
                     }),
                 })
             }
@@ -2069,8 +2110,9 @@ fn run_rustfmt(
     let edition = editions.iter().copied().max();
 
     let line_index = snap.file_line_index(file_id)?;
+    let sr = snap.analysis.source_root_id(file_id)?;
 
-    let mut command = match snap.config.rustfmt() {
+    let mut command = match snap.config.rustfmt(Some(sr)) {
         RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
             // FIXME: Set RUSTUP_TOOLCHAIN
             let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path());
@@ -2259,7 +2301,7 @@ pub(crate) fn internal_testing_fetch_config(
     serde_json::to_value(match &*params.config {
         "local" => state.config.assist(source_root).assist_emit_must_use,
         "global" => matches!(
-            state.config.rustfmt(),
+            state.config.rustfmt(source_root),
             RustfmtConfig::Rustfmt { enable_range_formatting: true, .. }
         ),
         _ => return Err(anyhow::anyhow!("Unknown test config key: {}", params.config)),
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 9a852067f2ea0..1fcb636f85606 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
@@ -75,14 +75,6 @@ impl Request for MemoryUsage {
     const METHOD: &'static str = "rust-analyzer/memoryUsage";
 }
 
-pub enum ShuffleCrateGraph {}
-
-impl Request for ShuffleCrateGraph {
-    type Params = ();
-    type Result = ();
-    const METHOD: &'static str = "rust-analyzer/shuffleCrateGraph";
-}
-
 pub enum ReloadWorkspace {}
 
 impl Request for ReloadWorkspace {
@@ -531,7 +523,7 @@ pub struct ServerStatusParams {
     pub message: Option,
 }
 
-#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
+#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)]
 #[serde(rename_all = "camelCase")]
 pub enum Health {
     Ok,
@@ -834,16 +826,3 @@ pub struct CompletionImport {
 pub struct ClientCommandOptions {
     pub commands: Vec,
 }
-
-pub enum UnindexedProject {}
-
-impl Notification for UnindexedProject {
-    type Params = UnindexedProjectParams;
-    const METHOD: &'static str = "rust-analyzer/unindexedProject";
-}
-
-#[derive(Deserialize, Serialize, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct UnindexedProjectParams {
-    pub text_documents: Vec,
-}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
index 60fe847bb7d87..aea424298f8c8 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
@@ -1,10 +1,7 @@
 //! Conversion lsp_types types to rust-analyzer specific ones.
 use anyhow::format_err;
 use ide::{Annotation, AnnotationKind, AssistKind, LineCol};
-use ide_db::{
-    base_db::{FileId, FilePosition, FileRange},
-    line_index::WideLineCol,
-};
+use ide_db::{line_index::WideLineCol, FileId, FilePosition, FileRange};
 use syntax::{TextRange, TextSize};
 use vfs::AbsPathBuf;
 
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 de394d3d11879..eb6bc2a9ce9b0 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
@@ -13,7 +13,7 @@ use ide::{
     NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
     SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
 };
-use ide_db::{rust_doc::format_docs, FxHasher};
+use ide_db::{assists, rust_doc::format_docs, FxHasher};
 use itertools::Itertools;
 use paths::{Utf8Component, Utf8Prefix};
 use semver::VersionReq;
@@ -1336,9 +1336,14 @@ pub(crate) fn code_action(
         command: None,
     };
 
-    if assist.trigger_signature_help && snap.config.client_commands().trigger_parameter_hints {
-        res.command = Some(command::trigger_parameter_hints());
-    }
+    let commands = snap.config.client_commands();
+    res.command = match assist.command {
+        Some(assists::Command::TriggerParameterHints) if commands.trigger_parameter_hints => {
+            Some(command::trigger_parameter_hints())
+        }
+        Some(assists::Command::Rename) if commands.rename => Some(command::rename()),
+        _ => None,
+    };
 
     match (assist.source_change, resolve_data) {
         (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
@@ -1394,7 +1399,11 @@ pub(crate) fn runnable(
                     cargo_args,
                     cwd: cwd.into(),
                     executable_args,
-                    environment: Default::default(),
+                    environment: spec
+                        .sysroot_root
+                        .map(|root| ("RUSTC_TOOLCHAIN".to_owned(), root.to_string()))
+                        .into_iter()
+                        .collect(),
                 }),
             }))
         }
@@ -1715,6 +1724,14 @@ pub(crate) mod command {
             arguments: None,
         }
     }
+
+    pub(crate) fn rename() -> lsp_types::Command {
+        lsp_types::Command {
+            title: "rename".into(),
+            command: "rust-analyzer.rename".into(),
+            arguments: None,
+        }
+    }
 }
 
 pub(crate) fn implementation_title(count: usize) -> String {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
index 800c0eee53a02..9a9e66be51ce3 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
@@ -74,13 +74,12 @@ impl GlobalState {
         }
     }
 
-    /// Sends a notification to the client containing the error `message`.
     /// If `additional_info` is [`Some`], appends a note to the notification telling to check the logs.
     /// This will always log `message` + `additional_info` to the server's error log.
     pub(crate) fn show_and_log_error(&mut self, message: String, additional_info: Option) {
         match additional_info {
             Some(additional_info) => {
-                tracing::error!("{}:\n{}", &message, &additional_info);
+                tracing::error!("{message}:\n{additional_info}");
                 self.show_message(
                     lsp_types::MessageType::ERROR,
                     message,
@@ -88,7 +87,7 @@ impl GlobalState {
                 );
             }
             None => {
-                tracing::error!("{}", &message);
+                tracing::error!("{message}");
                 self.send_notification::(
                     lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
                 );
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 07414a6e49cc3..9c820749ece6d 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
@@ -9,18 +9,19 @@ use std::{
 
 use always_assert::always;
 use crossbeam_channel::{select, Receiver};
+use flycheck::project_json;
 use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
 use lsp_server::{Connection, Notification, Request};
 use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
 use stdx::thread::ThreadIntent;
-use tracing::{span, Level};
-use vfs::FileId;
+use tracing::{error, span, Level};
+use vfs::{AbsPathBuf, FileId};
 
 use crate::{
     config::Config,
     diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration},
     dispatch::{NotificationDispatcher, RequestDispatcher},
-    global_state::{file_id_to_url, url_to_file_id, GlobalState},
+    global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState},
     hack_recover_crate_name,
     lsp::{
         from_proto, to_proto,
@@ -62,6 +63,7 @@ enum Event {
     Vfs(vfs::loader::Message),
     Flycheck(flycheck::Message),
     TestResult(flycheck::CargoTestMessage),
+    DiscoverProject(project_json::DiscoverProjectMessage),
 }
 
 impl fmt::Display for Event {
@@ -73,6 +75,7 @@ impl fmt::Display for Event {
             Event::Flycheck(_) => write!(f, "Event::Flycheck"),
             Event::QueuedTask(_) => write!(f, "Event::QueuedTask"),
             Event::TestResult(_) => write!(f, "Event::TestResult"),
+            Event::DiscoverProject(_) => write!(f, "Event::DiscoverProject"),
         }
     }
 }
@@ -86,7 +89,7 @@ pub(crate) enum QueuedTask {
 #[derive(Debug)]
 pub(crate) enum Task {
     Response(lsp_server::Response),
-    ClientNotification(lsp_ext::UnindexedProjectParams),
+    DiscoverLinkedProjects(DiscoverProjectParam),
     Retry(lsp_server::Request),
     Diagnostics(DiagnosticsGeneration, Vec<(FileId, Vec)>),
     DiscoverTest(lsp_ext::DiscoverTestResults),
@@ -97,6 +100,12 @@ pub(crate) enum Task {
     BuildDepsHaveChanged,
 }
 
+#[derive(Debug)]
+pub(crate) enum DiscoverProjectParam {
+    Buildfile(AbsPathBuf),
+    Path(AbsPathBuf),
+}
+
 #[derive(Debug)]
 pub(crate) enum PrimeCachesProgress {
     Begin,
@@ -134,6 +143,7 @@ impl fmt::Debug for Event {
             Event::Vfs(it) => fmt::Debug::fmt(it, f),
             Event::Flycheck(it) => fmt::Debug::fmt(it, f),
             Event::TestResult(it) => fmt::Debug::fmt(it, f),
+            Event::DiscoverProject(it) => fmt::Debug::fmt(it, f),
         }
     }
 }
@@ -143,14 +153,24 @@ impl GlobalState {
         self.update_status_or_notify();
 
         if self.config.did_save_text_document_dynamic_registration() {
-            self.register_did_save_capability();
+            let additional_patterns = self
+                .config
+                .discover_workspace_config()
+                .map(|cfg| cfg.files_to_watch.clone().into_iter())
+                .into_iter()
+                .flatten()
+                .map(|f| format!("**/{f}"));
+            self.register_did_save_capability(additional_patterns);
         }
 
-        self.fetch_workspaces_queue.request_op("startup".to_owned(), false);
-        if let Some((cause, force_crate_graph_reload)) =
-            self.fetch_workspaces_queue.should_start_op()
-        {
-            self.fetch_workspaces(cause, force_crate_graph_reload);
+        if self.config.discover_workspace_config().is_none() {
+            let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false };
+            self.fetch_workspaces_queue.request_op("startup".to_owned(), req);
+            if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =
+                self.fetch_workspaces_queue.should_start_op()
+            {
+                self.fetch_workspaces(cause, path, force_crate_graph_reload);
+            }
         }
 
         while let Some(event) = self.next_event(&inbox) {
@@ -167,32 +187,36 @@ impl GlobalState {
         anyhow::bail!("client exited without proper shutdown sequence")
     }
 
-    fn register_did_save_capability(&mut self) {
+    fn register_did_save_capability(&mut self, additional_patterns: impl Iterator) {
+        let additional_filters = additional_patterns.map(|pattern| lsp_types::DocumentFilter {
+            language: None,
+            scheme: None,
+            pattern: (Some(pattern)),
+        });
+
+        let mut selectors = vec![
+            lsp_types::DocumentFilter {
+                language: None,
+                scheme: None,
+                pattern: Some("**/*.rs".into()),
+            },
+            lsp_types::DocumentFilter {
+                language: None,
+                scheme: None,
+                pattern: Some("**/Cargo.toml".into()),
+            },
+            lsp_types::DocumentFilter {
+                language: None,
+                scheme: None,
+                pattern: Some("**/Cargo.lock".into()),
+            },
+        ];
+        selectors.extend(additional_filters);
+
         let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
             include_text: Some(false),
             text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
-                document_selector: Some(vec![
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/*.rs".into()),
-                    },
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/Cargo.toml".into()),
-                    },
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/Cargo.lock".into()),
-                    },
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/rust-analyzer.toml".into()),
-                    },
-                ]),
+                document_selector: Some(selectors),
             },
         };
 
@@ -230,6 +254,8 @@ impl GlobalState {
             recv(self.test_run_receiver) -> task =>
                 Some(Event::TestResult(task.unwrap())),
 
+            recv(self.discover_receiver) -> task =>
+                Some(Event::DiscoverProject(task.unwrap())),
         }
     }
 
@@ -340,6 +366,13 @@ impl GlobalState {
                     self.handle_cargo_test_msg(message);
                 }
             }
+            Event::DiscoverProject(message) => {
+                self.handle_discover_msg(message);
+                // Coalesce many project discovery events into a single loop turn.
+                while let Ok(message) = self.discover_receiver.try_recv() {
+                    self.handle_discover_msg(message);
+                }
+            }
         }
         let event_handling_duration = loop_start.elapsed();
 
@@ -427,11 +460,13 @@ impl GlobalState {
             }
         }
 
-        if self.config.cargo_autoreload_config() {
-            if let Some((cause, force_crate_graph_reload)) =
+        if self.config.cargo_autoreload_config()
+            || self.config.discover_workspace_config().is_some()
+        {
+            if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) =
                 self.fetch_workspaces_queue.should_start_op()
             {
-                self.fetch_workspaces(cause, force_crate_graph_reload);
+                self.fetch_workspaces(cause, path, force_crate_graph_reload);
             }
         }
 
@@ -606,9 +641,6 @@ impl GlobalState {
     fn handle_task(&mut self, prime_caches_progress: &mut Vec, task: Task) {
         match task {
             Task::Response(response) => self.respond(response),
-            Task::ClientNotification(params) => {
-                self.send_notification::(params)
-            }
             // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
             Task::Retry(req) if !self.is_completed(&req) => self.on_request(req),
             Task::Retry(_) => (),
@@ -638,7 +670,7 @@ impl GlobalState {
                         self.fetch_workspaces_queue
                             .op_completed(Some((workspaces, force_reload_crate_graph)));
                         if let Err(e) = self.fetch_workspace_error() {
-                            tracing::error!("FetchWorkspaceError:\n{e}");
+                            error!("FetchWorkspaceError:\n{e}");
                         }
                         self.switch_workspaces("fetched workspace".to_owned());
                         (Progress::End, None)
@@ -647,6 +679,35 @@ impl GlobalState {
 
                 self.report_progress("Fetching", state, msg, None, None);
             }
+            Task::DiscoverLinkedProjects(arg) => {
+                if let Some(cfg) = self.config.discover_workspace_config() {
+                    if !self.discover_workspace_queue.op_in_progress() {
+                        // the clone is unfortunately necessary to avoid a borrowck error when
+                        // `self.report_progress` is called later
+                        let title = &cfg.progress_label.clone();
+                        let command = cfg.command.clone();
+                        let discover =
+                            project_json::Discover::new(self.discover_sender.clone(), command);
+
+                        self.report_progress(title, Progress::Begin, None, None, None);
+                        self.discover_workspace_queue
+                            .request_op("Discovering workspace".to_owned(), ());
+                        let _ = self.discover_workspace_queue.should_start_op();
+
+                        let arg = match arg {
+                            DiscoverProjectParam::Buildfile(it) => {
+                                project_json::DiscoverArgument::Buildfile(it)
+                            }
+                            DiscoverProjectParam::Path(it) => {
+                                project_json::DiscoverArgument::Path(it)
+                            }
+                        };
+
+                        let handle = discover.spawn(arg).unwrap();
+                        self.discover_handle = Some(handle);
+                    }
+                }
+            }
             Task::FetchBuildData(progress) => {
                 let (state, msg) = match progress {
                     BuildDataProgress::Begin => (Some(Progress::Begin), None),
@@ -654,7 +715,7 @@ impl GlobalState {
                     BuildDataProgress::End(build_data_result) => {
                         self.fetch_build_data_queue.op_completed(build_data_result);
                         if let Err(e) = self.fetch_build_data_error() {
-                            tracing::error!("FetchBuildDataError:\n{e}");
+                            error!("FetchBuildDataError:\n{e}");
                         }
 
                         self.switch_workspaces("fetched build data".to_owned());
@@ -755,10 +816,12 @@ impl GlobalState {
                     let id = from_proto::file_id(&snap, &uri).expect("unable to get FileId");
                     if let Ok(crates) = &snap.analysis.crates_for(id) {
                         if crates.is_empty() {
-                            let params = lsp_ext::UnindexedProjectParams {
-                                text_documents: vec![lsp_types::TextDocumentIdentifier { uri }],
-                            };
-                            sender.send(Task::ClientNotification(params)).unwrap();
+                            if snap.config.discover_workspace_config().is_some() {
+                                let path =
+                                    from_proto::abs_path(&uri).expect("Unable to get AbsPath");
+                                let arg = DiscoverProjectParam::Path(path);
+                                sender.send(Task::DiscoverLinkedProjects(arg)).unwrap();
+                            }
                         } else {
                             tracing::debug!(?uri, "is indexed");
                         }
@@ -787,6 +850,33 @@ impl GlobalState {
         }
     }
 
+    fn handle_discover_msg(&mut self, message: project_json::DiscoverProjectMessage) {
+        let title = self
+            .config
+            .discover_workspace_config()
+            .map(|cfg| cfg.progress_label.clone())
+            .expect("No title could be found; this is a bug");
+        match message {
+            project_json::DiscoverProjectMessage::Finished { project, buildfile } => {
+                self.report_progress(&title, Progress::End, None, None, None);
+                self.discover_workspace_queue.op_completed(());
+
+                let mut config = Config::clone(&*self.config);
+                config.add_linked_projects(project, buildfile);
+                self.update_configuration(config);
+            }
+            project_json::DiscoverProjectMessage::Progress { message } => {
+                self.report_progress(&title, Progress::Report, Some(message), None, None)
+            }
+            project_json::DiscoverProjectMessage::Error { error, source } => {
+                let message = format!("Project discovery failed: {error}");
+                self.discover_workspace_queue.op_completed(());
+                self.show_and_log_error(message.clone(), source);
+                self.report_progress(&title, Progress::End, Some(message), None, None)
+            }
+        }
+    }
+
     fn handle_cargo_test_msg(&mut self, message: flycheck::CargoTestMessage) {
         match message {
             flycheck::CargoTestMessage::Test { name, state } => {
@@ -838,7 +928,7 @@ impl GlobalState {
                             diag.fix,
                         ),
                         Err(err) => {
-                            tracing::error!(
+                            error!(
                                 "flycheck {id}: File with cargo diagnostic not found in VFS: {}",
                                 err
                             );
@@ -928,7 +1018,6 @@ impl GlobalState {
             .on_sync_mut::(handlers::handle_workspace_reload)
             .on_sync_mut::(handlers::handle_proc_macros_rebuild)
             .on_sync_mut::(handlers::handle_memory_usage)
-            .on_sync_mut::(handlers::handle_shuffle_crate_graph)
             .on_sync_mut::(handlers::handle_run_test)
             // Request handlers which are related to the user typing
             // are run on the main thread to reduce latency:
@@ -964,10 +1053,10 @@ impl GlobalState {
             .on::(handlers::handle_goto_declaration)
             .on::(handlers::handle_goto_implementation)
             .on::(handlers::handle_goto_type_definition)
-            .on::(handlers::handle_inlay_hints)
-            .on::(handlers::handle_inlay_hints_resolve)
+            .on::(handlers::handle_inlay_hints)
+            .on::(handlers::handle_inlay_hints_resolve)
             .on::(handlers::handle_code_lens)
-            .on::(handlers::handle_code_lens_resolve)
+            .on::(handlers::handle_code_lens_resolve)
             .on::(handlers::handle_prepare_rename)
             .on::(handlers::handle_rename)
             .on::(handlers::handle_references)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
index 932730fc234b4..99f9e9829c936 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
@@ -3,6 +3,7 @@
 
 pub(crate) type Cause = String;
 
+#[derive(Debug)]
 pub(crate) struct OpQueue {
     op_requested: Option<(Cause, Args)>,
     op_in_progress: bool,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 1039daf850caa..5c95ccd4b826f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -16,8 +16,7 @@
 use std::{iter, mem};
 
 use flycheck::{FlycheckConfig, FlycheckHandle};
-use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros};
-use ide::CrateId;
+use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder};
 use ide_db::{
     base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, Version},
     FxHashMap,
@@ -33,11 +32,12 @@ use vfs::{AbsPath, AbsPathBuf, ChangeKind};
 
 use crate::{
     config::{Config, FilesWatcher, LinkedProject},
-    global_state::GlobalState,
+    global_state::{FetchWorkspaceRequest, GlobalState},
     lsp_ext,
-    main_loop::Task,
+    main_loop::{DiscoverProjectParam, Task},
     op_queue::Cause,
 };
+use tracing::{debug, info};
 
 #[derive(Debug)]
 pub(crate) enum ProjectWorkspaceProgress {
@@ -66,6 +66,7 @@ impl GlobalState {
             || self.fetch_workspaces_queue.op_in_progress()
             || self.fetch_build_data_queue.op_in_progress()
             || self.fetch_proc_macros_queue.op_in_progress()
+            || self.discover_workspace_queue.op_in_progress()
             || self.vfs_progress_config_version < self.vfs_config_version
             || self.vfs_progress_n_done < self.vfs_progress_n_total)
     }
@@ -81,9 +82,11 @@ impl GlobalState {
                 &self.config.lru_query_capacities_config().cloned().unwrap_or_default(),
             );
         }
+
         if self.config.linked_or_discovered_projects() != old_config.linked_or_discovered_projects()
         {
-            self.fetch_workspaces_queue.request_op("discovered projects changed".to_owned(), false)
+            let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false };
+            self.fetch_workspaces_queue.request_op("discovered projects changed".to_owned(), req)
         } else if self.config.flycheck() != old_config.flycheck() {
             self.reload_flycheck();
         }
@@ -106,9 +109,10 @@ impl GlobalState {
         };
         let mut message = String::new();
 
-        if !self.config.cargo_autoreload()
+        if !self.config.cargo_autoreload(None)
             && self.is_quiescent()
             && self.fetch_workspaces_queue.op_requested()
+            && self.config.discover_workspace_config().is_none()
         {
             status.health |= lsp_ext::Health::Warning;
             message.push_str("Auto-reloading is disabled and the workspace has changed, a manual workspace reload is required.\n\n");
@@ -124,7 +128,6 @@ impl GlobalState {
             status.health |= lsp_ext::Health::Warning;
             message.push_str("Failed to run build scripts of some packages.\n\n");
         }
-
         if let Some(err) = &self.config_errors {
             status.health |= lsp_ext::Health::Warning;
             format_to!(message, "{err}\n");
@@ -217,8 +220,13 @@ impl GlobalState {
         status
     }
 
-    pub(crate) fn fetch_workspaces(&mut self, cause: Cause, force_crate_graph_reload: bool) {
-        tracing::info!(%cause, "will fetch workspaces");
+    pub(crate) fn fetch_workspaces(
+        &mut self,
+        cause: Cause,
+        path: Option,
+        force_crate_graph_reload: bool,
+    ) {
+        info!(%cause, "will fetch workspaces");
 
         self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
             let linked_projects = self.config.linked_or_discovered_projects();
@@ -231,6 +239,10 @@ impl GlobalState {
                 .filter_map(Result::ok)
                 .collect();
             let cargo_config = self.config.cargo();
+            let discover_command = self.config.discover_workspace_config().cloned();
+            let is_quiescent = !(self.discover_workspace_queue.op_in_progress()
+                || self.vfs_progress_config_version < self.vfs_config_version
+                || self.vfs_progress_n_done < self.vfs_progress_n_total);
 
             move |sender| {
                 let progress = {
@@ -244,10 +256,28 @@ impl GlobalState {
 
                 sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();
 
+                if let (Some(_command), Some(path)) = (&discover_command, &path) {
+                    let build = linked_projects.iter().find_map(|project| match project {
+                        LinkedProject::InlineJsonProject(it) => it.crate_by_buildfile(path),
+                        _ => None,
+                    });
+
+                    if let Some(build) = build {
+                        if is_quiescent {
+                            let path = AbsPathBuf::try_from(build.build_file)
+                                .expect("Unable to convert to an AbsPath");
+                            let arg = DiscoverProjectParam::Buildfile(path);
+                            sender.send(Task::DiscoverLinkedProjects(arg)).unwrap();
+                        }
+                    }
+                }
+
                 let mut workspaces = linked_projects
                     .iter()
                     .map(|project| match project {
                         LinkedProject::ProjectManifest(manifest) => {
+                            debug!(path = %manifest, "loading project from manifest");
+
                             project_model::ProjectWorkspace::load(
                                 manifest.clone(),
                                 &cargo_config,
@@ -255,12 +285,13 @@ impl GlobalState {
                             )
                         }
                         LinkedProject::InlineJsonProject(it) => {
-                            Ok(project_model::ProjectWorkspace::load_inline(
+                            let workspace = project_model::ProjectWorkspace::load_inline(
                                 it.clone(),
                                 cargo_config.target.as_deref(),
                                 &cargo_config.extra_env,
                                 &cargo_config.cfg_overrides,
-                            ))
+                            );
+                            Ok(workspace)
                         }
                     })
                     .collect::>();
@@ -286,7 +317,7 @@ impl GlobalState {
                     ));
                 }
 
-                tracing::info!("did fetch workspaces {:?}", workspaces);
+                info!(?workspaces, "did fetch workspaces");
                 sender
                     .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(
                         workspaces,
@@ -298,7 +329,7 @@ impl GlobalState {
     }
 
     pub(crate) fn fetch_build_data(&mut self, cause: Cause) {
-        tracing::info!(%cause, "will fetch build data");
+        info!(%cause, "will fetch build data");
         let workspaces = Arc::clone(&self.workspaces);
         let config = self.config.cargo();
         let root_path = self.config.root_path().clone();
@@ -324,7 +355,7 @@ impl GlobalState {
     }
 
     pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) {
-        tracing::info!(%cause, "will load proc macros");
+        info!(%cause, "will load proc macros");
         let ignored_proc_macros = self.config.ignored_proc_macros().clone();
         let proc_macro_clients = self.proc_macro_clients.clone();
 
@@ -339,43 +370,44 @@ impl GlobalState {
                 }
             };
 
-            let mut res = FxHashMap::default();
+            let mut builder = ProcMacrosBuilder::default();
             let chain = proc_macro_clients
                 .iter()
                 .map(|res| res.as_ref().map_err(|e| e.to_string()))
-                .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into())));
+                .chain(iter::repeat_with(|| Err("proc-macro-srv is not running".into())));
             for (client, paths) in chain.zip(paths) {
-                res.extend(paths.into_iter().map(move |(crate_id, res)| {
-                    (
-                        crate_id,
-                        res.map_or_else(
-                            |_| Err("proc macro crate is missing dylib".to_owned()),
-                            |(crate_name, path)| {
-                                progress(path.to_string());
-                                client.as_ref().map_err(Clone::clone).and_then(|client| {
-                                    load_proc_macro(
-                                        client,
-                                        &path,
-                                        crate_name
-                                            .as_deref()
-                                            .and_then(|crate_name| {
-                                                ignored_proc_macros.iter().find_map(
-                                                    |(name, macros)| {
-                                                        eq_ignore_underscore(name, crate_name)
+                paths
+                    .into_iter()
+                    .map(move |(crate_id, res)| {
+                        (
+                            crate_id,
+                            res.map_or_else(
+                                |e| Err((e, true)),
+                                |(crate_name, path)| {
+                                    progress(path.to_string());
+                                    client.as_ref().map_err(|it| (it.clone(), true)).and_then(
+                                        |client| {
+                                            load_proc_macro(
+                                                client,
+                                                &path,
+                                                ignored_proc_macros
+                                                    .iter()
+                                                    .find_map(|(name, macros)| {
+                                                        eq_ignore_underscore(name, &crate_name)
                                                             .then_some(&**macros)
-                                                    },
-                                                )
-                                            })
-                                            .unwrap_or_default(),
+                                                    })
+                                                    .unwrap_or_default(),
+                                            )
+                                        },
                                     )
-                                })
-                            },
-                        ),
-                    )
-                }));
+                                },
+                            ),
+                        )
+                    })
+                    .for_each(|(krate, res)| builder.insert(krate, res));
             }
 
-            sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap();
+            sender.send(Task::LoadProcMacros(ProcMacroProgress::End(builder.build()))).unwrap();
         });
     }
 
@@ -395,6 +427,7 @@ impl GlobalState {
             return;
         };
 
+        info!(%cause, ?force_reload_crate_graph);
         if self.fetch_workspace_error().is_err() && !self.workspaces.is_empty() {
             if *force_reload_crate_graph {
                 self.recreate_crate_graph(cause);
@@ -416,7 +449,7 @@ impl GlobalState {
         if same_workspaces {
             let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result();
             if Arc::ptr_eq(workspaces, &self.workspaces) {
-                tracing::debug!("set build scripts to workspaces");
+                info!("set build scripts to workspaces");
 
                 let workspaces = workspaces
                     .iter()
@@ -428,9 +461,10 @@ impl GlobalState {
                     })
                     .collect::>();
                 // Workspaces are the same, but we've updated build data.
+                info!("same workspace, but new build data");
                 self.workspaces = Arc::new(workspaces);
             } else {
-                tracing::info!("build scripts do not match the version of the active workspace");
+                info!("build scripts do not match the version of the active workspace");
                 if *force_reload_crate_graph {
                     self.recreate_crate_graph(cause);
                 }
@@ -440,7 +474,7 @@ impl GlobalState {
                 return;
             }
         } else {
-            tracing::debug!("abandon build scripts for workspaces");
+            info!("abandon build scripts for workspaces");
 
             // Here, we completely changed the workspace (Cargo.toml edit), so
             // we don't care about build-script results, they are stale.
@@ -507,7 +541,6 @@ impl GlobalState {
 
             watchers.extend(
                 iter::once(self.config.user_config_path().as_path())
-                    .chain(iter::once(self.config.root_ratoml_path().as_path()))
                     .chain(self.workspaces.iter().map(|ws| ws.manifest().map(ManifestPath::as_ref)))
                     .flatten()
                     .map(|glob_pattern| lsp_types::FileSystemWatcher {
@@ -535,7 +568,7 @@ impl GlobalState {
         if (self.proc_macro_clients.is_empty() || !same_workspaces)
             && self.config.expand_proc_macros()
         {
-            tracing::info!("Spawning proc-macro servers");
+            info!("Spawning proc-macro servers");
 
             self.proc_macro_clients = Arc::from_iter(self.workspaces.iter().map(|ws| {
                 let path = match self.config.proc_macro_srv() {
@@ -562,7 +595,7 @@ impl GlobalState {
 
                     _ => Default::default(),
                 };
-                tracing::info!("Using proc-macro server at {path}");
+                info!("Using proc-macro server at {path}");
 
                 ProcMacroServer::spawn(&path, &env).map_err(|err| {
                     tracing::error!(
@@ -588,12 +621,14 @@ impl GlobalState {
         self.source_root_config = project_folders.source_root_config;
         self.local_roots_parent_map = Arc::new(self.source_root_config.source_root_parent_map());
 
+        info!(?cause, "recreating the crate graph");
         self.recreate_crate_graph(cause);
 
-        tracing::info!("did switch workspaces");
+        info!("did switch workspaces");
     }
 
     fn recreate_crate_graph(&mut self, cause: String) {
+        info!(?cause, "Building Crate Graph");
         self.report_progress(
             "Building CrateGraph",
             crate::lsp::utils::Progress::Begin,
@@ -631,10 +666,17 @@ impl GlobalState {
             change.set_proc_macros(
                 crate_graph
                     .iter()
-                    .map(|id| (id, Err("Proc-macros have not been built yet".to_owned())))
+                    .map(|id| (id, Err(("proc-macro has not been built yet".to_owned(), true))))
                     .collect(),
             );
             self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths);
+        } else {
+            change.set_proc_macros(
+                crate_graph
+                    .iter()
+                    .map(|id| (id, Err(("proc-macro expansion is disabled".to_owned(), false))))
+                    .collect(),
+            );
         }
         change.set_crate_graph(crate_graph);
         change.set_target_data_layouts(layouts);
@@ -658,12 +700,19 @@ impl GlobalState {
         let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else {
             return Ok(());
         };
-        if last_op_result.is_empty() {
-            stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
-        } else {
-            for ws in last_op_result {
-                if let Err(err) = ws {
-                    stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
+
+        if !self.discover_workspace_queue.op_in_progress() {
+            if last_op_result.is_empty() {
+                stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
+            } else {
+                for ws in last_op_result {
+                    if let Err(err) = ws {
+                        stdx::format_to!(
+                            buf,
+                            "rust-analyzer failed to load workspace: {:#}\n",
+                            err
+                        );
+                    }
                 }
             }
         }
@@ -766,12 +815,7 @@ pub fn ws_to_crate_graph(
     workspaces: &[ProjectWorkspace],
     extra_env: &FxHashMap,
     mut load: impl FnMut(&AbsPath) -> Option,
-) -> (
-    CrateGraph,
-    Vec, AbsPathBuf), String>>>,
-    Vec, Arc>>,
-    Vec>,
-) {
+) -> (CrateGraph, Vec, Vec, Arc>>, Vec>) {
     let mut crate_graph = CrateGraph::default();
     let mut proc_macro_paths = Vec::default();
     let mut layouts = Vec::default();
@@ -818,7 +862,11 @@ pub fn ws_to_crate_graph(
     (crate_graph, proc_macro_paths, layouts, toolchains)
 }
 
-pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
+pub(crate) fn should_refresh_for_change(
+    path: &AbsPath,
+    change_kind: ChangeKind,
+    additional_paths: &[&str],
+) -> bool {
     const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
     const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
 
@@ -830,6 +878,11 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
     if let "Cargo.toml" | "Cargo.lock" = file_name {
         return true;
     }
+
+    if additional_paths.contains(&file_name) {
+        return true;
+    }
+
     if change_kind == ChangeKind::Modify {
         return false;
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
index 863ff06439961..67e1bad5281d0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
@@ -3,6 +3,7 @@
 use std::mem;
 
 use cfg::{CfgAtom, CfgExpr};
+use hir::sym;
 use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId};
 use project_model::project_json::Runnable;
 use project_model::{CargoFeatures, ManifestPath, TargetKind};
@@ -56,6 +57,7 @@ pub(crate) struct CargoTargetSpec {
     pub(crate) crate_id: CrateId,
     pub(crate) required_features: Vec,
     pub(crate) features: FxHashSet,
+    pub(crate) sysroot_root: Option,
 }
 
 #[derive(Clone, Debug)]
@@ -237,14 +239,14 @@ impl CargoTargetSpec {
 /// Fill minimal features needed
 fn required_features(cfg_expr: &CfgExpr, features: &mut Vec) {
     match cfg_expr {
-        CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if key == "feature" => {
+        CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if *key == sym::feature => {
             features.push(value.to_string())
         }
         CfgExpr::All(preds) => {
             preds.iter().for_each(|cfg| required_features(cfg, features));
         }
         CfgExpr::Any(preds) => {
-            for cfg in preds {
+            for cfg in preds.iter() {
                 let len_features = features.len();
                 required_features(cfg, features);
                 if len_features != features.len() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
index fcdbf6c694979..f330754f19a1e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
@@ -48,7 +48,10 @@ where
 
         let writer = self.writer;
 
-        let ra_fmt_layer = tracing_subscriber::fmt::layer().with_writer(writer).with_filter(filter);
+        let ra_fmt_layer = tracing_subscriber::fmt::layer()
+            .with_target(false)
+            .with_writer(writer)
+            .with_filter(filter);
 
         let mut chalk_layer = None;
         if let Some(chalk_filter) = self.chalk_filter {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 56f416a0b6e04..b1ef48377178b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -27,8 +27,7 @@ use lsp_types::{
     InlayHint, InlayHintLabel, InlayHintParams, PartialResultParams, Position, Range,
     RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams,
 };
-
-use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams, UnindexedProject};
+use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams};
 use serde_json::json;
 use stdx::format_to_acc;
 
@@ -812,66 +811,6 @@ fn main() {{}}
     );
 }
 
-#[test]
-fn test_opening_a_file_outside_of_indexed_workspace() {
-    if skip_slow_tests() {
-        return;
-    }
-
-    let tmp_dir = TestDir::new();
-    let path = tmp_dir.path();
-
-    let project = json!({
-        "roots": [path],
-        "crates": [ {
-            "root_module": path.join("src/crate_one/lib.rs"),
-            "deps": [],
-            "edition": "2015",
-            "cfg": [ "cfg_atom_1", "feature=\"cfg_1\""],
-        } ]
-    });
-
-    let code = format!(
-        r#"
-//- /rust-project.json
-{project}
-
-//- /src/crate_one/lib.rs
-mod bar;
-
-fn main() {{}}
-"#,
-    );
-
-    let server = Project::with_fixture(&code)
-        .tmp_dir(tmp_dir)
-        .with_config(serde_json::json!({
-            "notifications": {
-                "unindexedProject": true
-            },
-        }))
-        .server()
-        .wait_until_workspace_is_loaded();
-
-    let uri = server.doc_id("src/crate_two/lib.rs").uri;
-    server.notification::(DidOpenTextDocumentParams {
-        text_document: TextDocumentItem {
-            uri: uri.clone(),
-            language_id: "rust".to_owned(),
-            version: 0,
-            text: "/// Docs\nfn foo() {}".to_owned(),
-        },
-    });
-    let expected = json!({
-        "textDocuments": [
-            {
-                "uri": uri
-            }
-        ]
-    });
-    server.expect_notification::(expected);
-}
-
 #[test]
 fn diagnostics_dont_block_typing() {
     if skip_slow_tests() {
@@ -970,7 +909,7 @@ version = \"0.0.0\"
 
 fn out_dirs_check_impl(root_contains_symlink: bool) {
     if skip_slow_tests() {
-        return;
+        // return;
     }
 
     let mut server = Project::with_fixture(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
index 218a9a32adbae..c06ba9eee1477 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -10,6 +10,7 @@ use paths::Utf8PathBuf;
 
 use rust_analyzer::lsp::ext::{InternalTestingFetchConfig, InternalTestingFetchConfigParams};
 use serde_json::json;
+use test_utils::skip_slow_tests;
 
 enum QueryType {
     Local,
@@ -30,8 +31,6 @@ impl RatomlTest {
     const EMIT_MUST_USE: &'static str = r#"assist.emitMustUse = true"#;
     const EMIT_MUST_NOT_USE: &'static str = r#"assist.emitMustUse = false"#;
 
-    const GLOBAL_TRAIT_ASSOC_ITEMS_ZERO: &'static str = r#"hover.show.traitAssocItems = 0"#;
-
     fn new(
         fixtures: Vec<&str>,
         roots: Vec<&str>,
@@ -180,29 +179,14 @@ impl RatomlTest {
     }
 }
 
-// /// Check if we are listening for changes in user's config file ( e.g on Linux `~/.config/rust-analyzer/.rust-analyzer.toml`)
-// #[test]
-// #[cfg(target_os = "windows")]
-// fn listen_to_user_config_scenario_windows() {
-//     todo!()
-// }
-
-// #[test]
-// #[cfg(target_os = "linux")]
-// fn listen_to_user_config_scenario_linux() {
-//     todo!()
-// }
-
-// #[test]
-// #[cfg(target_os = "macos")]
-// fn listen_to_user_config_scenario_macos() {
-//     todo!()
-// }
-
 /// Check if made changes have had any effect on
 /// the client config.
 #[test]
 fn ratoml_client_config_basic() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let server = RatomlTest::new(
         vec![
             r#"
@@ -304,6 +288,10 @@ enum Value {
 #[test]
 #[ignore = "the user config is currently not being watched on startup, fix this"]
 fn ratoml_user_config_detected() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let server = RatomlTest::new(
         vec![
             r#"
@@ -333,6 +321,10 @@ enum Value {
 #[test]
 #[ignore = "the user config is currently not being watched on startup, fix this"]
 fn ratoml_create_user_config() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -364,6 +356,10 @@ enum Value {
 #[test]
 #[ignore = "the user config is currently not being watched on startup, fix this"]
 fn ratoml_modify_user_config() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -394,6 +390,10 @@ assist.emitMustUse = true"#,
 #[test]
 #[ignore = "the user config is currently not being watched on startup, fix this"]
 fn ratoml_delete_user_config() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -420,18 +420,13 @@ assist.emitMustUse = true"#,
     server.delete(2);
     assert!(!server.query(QueryType::Local, 1));
 }
-// #[test]
-// fn delete_user_config() {
-//     todo!()
-// }
-
-// #[test]
-// fn modify_client_config() {
-//     todo!()
-// }
 
 #[test]
 fn ratoml_inherit_config_from_ws_root() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let server = RatomlTest::new(
         vec![
             r#"
@@ -475,6 +470,10 @@ pub fn add(left: usize, right: usize) -> usize {
 
 #[test]
 fn ratoml_modify_ratoml_at_ws_root() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -520,6 +519,10 @@ pub fn add(left: usize, right: usize) -> usize {
 
 #[test]
 fn ratoml_delete_ratoml_at_ws_root() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -565,6 +568,10 @@ pub fn add(left: usize, right: usize) -> usize {
 
 #[test]
 fn ratoml_add_immediate_child_to_ws_root() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -609,7 +616,12 @@ pub fn add(left: usize, right: usize) -> usize {
 }
 
 #[test]
+#[ignore = "Root ratomls are not being looked for on startup. Fix this."]
 fn ratoml_rm_ws_root_ratoml_child_has_client_as_parent_now() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -655,6 +667,10 @@ pub fn add(left: usize, right: usize) -> usize {
 
 #[test]
 fn ratoml_crates_both_roots() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let server = RatomlTest::new(
         vec![
             r#"
@@ -699,6 +715,10 @@ enum Value {
 
 #[test]
 fn ratoml_multiple_ratoml_in_single_source_root() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let server = RatomlTest::new(
         vec![
             r#"
@@ -729,37 +749,6 @@ fn ratoml_multiple_ratoml_in_single_source_root() {
     );
 
     assert!(server.query(QueryType::Local, 3));
-
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/src/rust-analyzer.toml
-assist.emitMustUse = false
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::Local, 3));
 }
 
 /// If a root is non-local, so we cannot find what its parent is
@@ -838,6 +827,10 @@ enum Value {
 /// configuring global level configurations as well.
 #[test]
 fn ratoml_in_root_is_global() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let server = RatomlTest::new(
         vec![
             r#"
@@ -848,32 +841,28 @@ version = "0.1.0"
 edition = "2021"
         "#,
             r#"
-//- /rust-analyzer.toml
-hover.show.traitAssocItems = 4
+//- /p1/rust-analyzer.toml
+rustfmt.rangeFormatting.enable = true
         "#,
             r#"
 //- /p1/src/lib.rs
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-
 fn main() {
-    let a = RandomTrait;
+    todo!()
 }"#,
         ],
-        vec![],
+        vec!["p1"],
         None,
     );
 
-    server.query(QueryType::Global, 2);
+    assert!(server.query(QueryType::Global, 2));
 }
 
-#[allow(unused)]
-// #[test]
-// FIXME: Re-enable this test when we have a global config we can check again
+#[test]
 fn ratoml_root_is_updateable() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -884,34 +873,30 @@ version = "0.1.0"
 edition = "2021"
         "#,
             r#"
-//- /rust-analyzer.toml
-hover.show.traitAssocItems = 4
-        "#,
+//- /p1/rust-analyzer.toml
+rustfmt.rangeFormatting.enable = true
+    "#,
             r#"
 //- /p1/src/lib.rs
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-
 fn main() {
-    let a = RandomTrait;
+   todo!()
 }"#,
         ],
-        vec![],
+        vec!["p1"],
         None,
     );
 
     assert!(server.query(QueryType::Global, 2));
-    server.edit(1, RatomlTest::GLOBAL_TRAIT_ASSOC_ITEMS_ZERO.to_owned());
+    server.edit(1, "rustfmt.rangeFormatting.enable = false".to_owned());
     assert!(!server.query(QueryType::Global, 2));
 }
 
-#[allow(unused)]
-// #[test]
-// FIXME: Re-enable this test when we have a global config we can check again
+#[test]
 fn ratoml_root_is_deletable() {
+    if skip_slow_tests() {
+        return;
+    }
+
     let mut server = RatomlTest::new(
         vec![
             r#"
@@ -922,22 +907,16 @@ version = "0.1.0"
 edition = "2021"
         "#,
             r#"
-//- /rust-analyzer.toml
-hover.show.traitAssocItems = 4
-        "#,
+//- /p1/rust-analyzer.toml
+rustfmt.rangeFormatting.enable = true
+       "#,
             r#"
 //- /p1/src/lib.rs
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-
 fn main() {
-    let a = RandomTrait;
+    todo!()
 }"#,
         ],
-        vec![],
+        vec!["p1"],
         None,
     );
 
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 66100971fbf34..081ee5fa3e4e7 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
@@ -256,40 +256,6 @@ impl Server {
         self.send_notification(r)
     }
 
-    pub(crate) fn expect_notification(&self, expected: Value)
-    where
-        N: lsp_types::notification::Notification,
-        N::Params: Serialize,
-    {
-        while let Some(Message::Notification(actual)) =
-            recv_timeout(&self.client.receiver).unwrap_or_else(|_| panic!("timed out"))
-        {
-            if actual.method == N::METHOD {
-                let actual = actual
-                    .clone()
-                    .extract::(N::METHOD)
-                    .expect("was not able to extract notification");
-
-                tracing::debug!(?actual, "got notification");
-                if let Some((expected_part, actual_part)) = find_mismatch(&expected, &actual) {
-                    panic!(
-                            "JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n",
-                            to_string_pretty(&expected).unwrap(),
-                            to_string_pretty(&actual).unwrap(),
-                            to_string_pretty(expected_part).unwrap(),
-                            to_string_pretty(actual_part).unwrap(),
-                        );
-                } else {
-                    tracing::debug!("successfully matched notification");
-                    return;
-                }
-            } else {
-                continue;
-            }
-        }
-        panic!("never got expected notification");
-    }
-
     #[track_caller]
     pub(crate) fn request(&self, params: R::Params, expected_resp: Value)
     where
diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
index 4e70741239734..eeaf008a15c43 100644
--- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
+++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs
@@ -53,7 +53,11 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
                         num_storages += 1;
                     }
                     "dependencies" => {
-                        storage = QueryStorage::Dependencies;
+                        storage = QueryStorage::LruDependencies;
+                        num_storages += 1;
+                    }
+                    "lru" => {
+                        storage = QueryStorage::LruMemoized;
                         num_storages += 1;
                     }
                     "input" => {
@@ -235,7 +239,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
 
         queries_with_storage.push(fn_name);
 
-        let tracing = if let QueryStorage::Memoized = query.storage {
+        let tracing = if let QueryStorage::Memoized | QueryStorage::LruMemoized = query.storage {
             let s = format!("{trait_name}::{fn_name}");
             Some(quote! {
                 let _p = tracing::debug_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered();
@@ -376,8 +380,9 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
 
         let storage = match &query.storage {
             QueryStorage::Memoized => quote!(salsa::plumbing::MemoizedStorage),
-            QueryStorage::Dependencies => {
-                quote!(salsa::plumbing::DependencyStorage)
+            QueryStorage::LruMemoized => quote!(salsa::plumbing::LruMemoizedStorage),
+            QueryStorage::LruDependencies => {
+                quote!(salsa::plumbing::LruDependencyStorage)
             }
             QueryStorage::Input if query.keys.is_empty() => {
                 quote!(salsa::plumbing::UnitInputStorage)
@@ -724,7 +729,8 @@ impl Query {
 #[derive(Debug, Clone, PartialEq, Eq)]
 enum QueryStorage {
     Memoized,
-    Dependencies,
+    LruDependencies,
+    LruMemoized,
     Input,
     Interned,
     InternedLookup { intern_query_type: Ident },
@@ -739,7 +745,9 @@ impl QueryStorage {
             | QueryStorage::Interned
             | QueryStorage::InternedLookup { .. }
             | QueryStorage::Transparent => false,
-            QueryStorage::Memoized | QueryStorage::Dependencies => true,
+            QueryStorage::Memoized | QueryStorage::LruMemoized | QueryStorage::LruDependencies => {
+                true
+            }
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived.rs b/src/tools/rust-analyzer/crates/salsa/src/derived.rs
index fd31ab204169c..8b2fdd6b19cc8 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/derived.rs
@@ -1,9 +1,7 @@
 use crate::debug::TableEntry;
 use crate::durability::Durability;
 use crate::hash::FxIndexMap;
-use crate::lru::Lru;
 use crate::plumbing::DerivedQueryStorageOps;
-use crate::plumbing::LruQueryStorageOps;
 use crate::plumbing::QueryFunction;
 use crate::plumbing::QueryStorageMassOps;
 use crate::plumbing::QueryStorageOps;
@@ -13,7 +11,6 @@ use crate::{Database, DatabaseKeyIndex, QueryDb, Revision};
 use parking_lot::RwLock;
 use std::borrow::Borrow;
 use std::hash::Hash;
-use std::marker::PhantomData;
 use triomphe::Arc;
 
 mod slot;
@@ -22,79 +19,33 @@ use slot::Slot;
 /// Memoized queries store the result plus a list of the other queries
 /// that they invoked. This means we can avoid recomputing them when
 /// none of those inputs have changed.
-pub type MemoizedStorage = DerivedStorage;
-
-/// "Dependency" queries just track their dependencies and not the
-/// actual value (which they produce on demand). This lessens the
-/// storage requirements.
-pub type DependencyStorage = DerivedStorage;
+pub type MemoizedStorage = DerivedStorage;
 
 /// Handles storage where the value is 'derived' by executing a
 /// function (in contrast to "inputs").
-pub struct DerivedStorage
+pub struct DerivedStorage
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
 {
     group_index: u16,
-    lru_list: Lru>,
-    slot_map: RwLock>>>,
-    policy: PhantomData,
+    slot_map: RwLock>>>,
 }
 
-impl std::panic::RefUnwindSafe for DerivedStorage
+impl std::panic::RefUnwindSafe for DerivedStorage
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+
     Q::Key: std::panic::RefUnwindSafe,
     Q::Value: std::panic::RefUnwindSafe,
 {
 }
 
-pub trait MemoizationPolicy: Send + Sync
-where
-    Q: QueryFunction,
-{
-    fn should_memoize_value(key: &Q::Key) -> bool;
-
-    fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool;
-}
-
-pub enum AlwaysMemoizeValue {}
-impl MemoizationPolicy for AlwaysMemoizeValue
+impl DerivedStorage
 where
     Q: QueryFunction,
     Q::Value: Eq,
 {
-    fn should_memoize_value(_key: &Q::Key) -> bool {
-        true
-    }
-
-    fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool {
-        old_value == new_value
-    }
-}
-
-pub enum NeverMemoizeValue {}
-impl MemoizationPolicy for NeverMemoizeValue
-where
-    Q: QueryFunction,
-{
-    fn should_memoize_value(_key: &Q::Key) -> bool {
-        false
-    }
-
-    fn memoized_value_eq(_old_value: &Q::Value, _new_value: &Q::Value) -> bool {
-        panic!("cannot reach since we never memoize")
-    }
-}
-
-impl DerivedStorage
-where
-    Q: QueryFunction,
-    MP: MemoizationPolicy,
-{
-    fn slot(&self, key: &Q::Key) -> Arc> {
+    fn slot(&self, key: &Q::Key) -> Arc> {
         if let Some(v) = self.slot_map.read().get(key) {
             return v.clone();
         }
@@ -111,20 +62,15 @@ where
     }
 }
 
-impl QueryStorageOps for DerivedStorage
+impl QueryStorageOps for DerivedStorage
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+    Q::Value: Eq,
 {
     const CYCLE_STRATEGY: crate::plumbing::CycleRecoveryStrategy = Q::CYCLE_STRATEGY;
 
     fn new(group_index: u16) -> Self {
-        DerivedStorage {
-            group_index,
-            slot_map: RwLock::new(FxIndexMap::default()),
-            lru_list: Default::default(),
-            policy: PhantomData,
-        }
+        DerivedStorage { group_index, slot_map: RwLock::new(FxIndexMap::default()) }
     }
 
     fn fmt_index(
@@ -161,10 +107,6 @@ where
         let slot = self.slot(key);
         let StampedValue { value, durability, changed_at } = slot.read(db, key);
 
-        if let Some(evicted) = self.lru_list.record_use(&slot) {
-            evicted.evict();
-        }
-
         db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted(
             slot.database_key_index(),
             durability,
@@ -175,7 +117,7 @@ where
     }
 
     fn durability(&self, db: &>::DynDb, key: &Q::Key) -> Durability {
-        self.slot(key).durability(db)
+        self.slot_map.read().get(key).map_or(Durability::LOW, |slot| slot.durability(db))
     }
 
     fn entries(&self, _db: &>::DynDb) -> C
@@ -187,31 +129,19 @@ where
     }
 }
 
-impl QueryStorageMassOps for DerivedStorage
+impl QueryStorageMassOps for DerivedStorage
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
 {
     fn purge(&self) {
-        self.lru_list.purge();
         *self.slot_map.write() = Default::default();
     }
 }
 
-impl LruQueryStorageOps for DerivedStorage
-where
-    Q: QueryFunction,
-    MP: MemoizationPolicy,
-{
-    fn set_lru_capacity(&self, new_capacity: usize) {
-        self.lru_list.set_lru_capacity(new_capacity);
-    }
-}
-
-impl DerivedQueryStorageOps for DerivedStorage
+impl DerivedQueryStorageOps for DerivedStorage
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+    Q::Value: Eq,
 {
     fn invalidate(&self, runtime: &mut Runtime, key: &S)
     where
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
index cfafa40ce33ad..de7a39760746c 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs
@@ -1,12 +1,8 @@
 use crate::debug::TableEntry;
-use crate::derived::MemoizationPolicy;
 use crate::durability::Durability;
-use crate::lru::LruIndex;
-use crate::lru::LruNode;
 use crate::plumbing::{DatabaseOps, QueryFunction};
 use crate::revision::Revision;
 use crate::runtime::local_state::ActiveQueryGuard;
-use crate::runtime::local_state::QueryInputs;
 use crate::runtime::local_state::QueryRevisions;
 use crate::runtime::Runtime;
 use crate::runtime::RuntimeId;
@@ -15,21 +11,18 @@ use crate::runtime::WaitResult;
 use crate::Cycle;
 use crate::{Database, DatabaseKeyIndex, Event, EventKind, QueryDb};
 use parking_lot::{RawRwLock, RwLock};
-use std::marker::PhantomData;
 use std::ops::Deref;
 use std::sync::atomic::{AtomicBool, Ordering};
 use tracing::{debug, info};
 
-pub(super) struct Slot
+pub(super) struct Slot
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
 {
     key_index: u32,
+    // FIXME: Yeet this
     group_index: u16,
     state: RwLock>,
-    policy: PhantomData,
-    lru_index: LruIndex,
 }
 
 /// Defines the "current state" of query's memoized results.
@@ -55,7 +48,7 @@ where
 
 struct Memo {
     /// The result of the query, if we decide to memoize it.
-    value: Option,
+    value: V,
 
     /// Last revision when this memo was verified; this begins
     /// as the current revision.
@@ -78,12 +71,6 @@ enum ProbeState {
     /// verified in this revision.
     Stale(G),
 
-    /// There is an entry, and it has been verified
-    /// in this revision, but it has no cached
-    /// value. The `Revision` is the revision where the
-    /// value last changed (if we were to recompute it).
-    NoValue(G, Revision),
-
     /// There is an entry which has been verified,
     /// and it has the following value-- or, we blocked
     /// on another thread, and that resulted in a cycle.
@@ -104,18 +91,16 @@ enum MaybeChangedSinceProbeState {
     Stale(G),
 }
 
-impl Slot
+impl Slot
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+    Q::Value: Eq,
 {
     pub(super) fn new(database_key_index: DatabaseKeyIndex) -> Self {
         Self {
             key_index: database_key_index.key_index,
             group_index: database_key_index.group_index,
             state: RwLock::new(QueryState::NotComputed),
-            lru_index: LruIndex::default(),
-            policy: PhantomData,
         }
     }
 
@@ -147,9 +132,7 @@ where
         loop {
             match self.probe(db, self.state.read(), runtime, revision_now) {
                 ProbeState::UpToDate(v) => return v,
-                ProbeState::Stale(..) | ProbeState::NoValue(..) | ProbeState::NotComputed(..) => {
-                    break
-                }
+                ProbeState::Stale(..) | ProbeState::NotComputed(..) => break,
                 ProbeState::Retry => continue,
             }
         }
@@ -177,9 +160,7 @@ where
         let mut old_memo = loop {
             match self.probe(db, self.state.upgradable_read(), runtime, revision_now) {
                 ProbeState::UpToDate(v) => return v,
-                ProbeState::Stale(state)
-                | ProbeState::NotComputed(state)
-                | ProbeState::NoValue(state, _) => {
+                ProbeState::Stale(state) | ProbeState::NotComputed(state) => {
                     type RwLockUpgradableReadGuard<'a, T> =
                         lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>;
 
@@ -227,7 +208,7 @@ where
         runtime: &Runtime,
         revision_now: Revision,
         active_query: ActiveQueryGuard<'_>,
-        panic_guard: PanicGuard<'_, Q, MP>,
+        panic_guard: PanicGuard<'_, Q>,
         old_memo: Option>,
         key: &Q::Key,
     ) -> StampedValue {
@@ -286,22 +267,18 @@ where
         // "backdate" its `changed_at` revision to be the same as the
         // old value.
         if let Some(old_memo) = &old_memo {
-            if let Some(old_value) = &old_memo.value {
-                // Careful: if the value became less durable than it
-                // used to be, that is a "breaking change" that our
-                // consumers must be aware of. Becoming *more* durable
-                // is not. See the test `constant_to_non_constant`.
-                if revisions.durability >= old_memo.revisions.durability
-                    && MP::memoized_value_eq(old_value, &value)
-                {
-                    debug!(
-                        "read_upgrade({:?}): value is equal, back-dating to {:?}",
-                        self, old_memo.revisions.changed_at,
-                    );
-
-                    assert!(old_memo.revisions.changed_at <= revisions.changed_at);
-                    revisions.changed_at = old_memo.revisions.changed_at;
-                }
+            // Careful: if the value became less durable than it
+            // used to be, that is a "breaking change" that our
+            // consumers must be aware of. Becoming *more* durable
+            // is not. See the test `constant_to_non_constant`.
+            if revisions.durability >= old_memo.revisions.durability && old_memo.value == value {
+                debug!(
+                    "read_upgrade({:?}): value is equal, back-dating to {:?}",
+                    self, old_memo.revisions.changed_at,
+                );
+
+                assert!(old_memo.revisions.changed_at <= revisions.changed_at);
+                revisions.changed_at = old_memo.revisions.changed_at;
             }
         }
 
@@ -311,8 +288,7 @@ where
             changed_at: revisions.changed_at,
         };
 
-        let memo_value =
-            if self.should_memoize_value(key) { Some(new_value.value.clone()) } else { None };
+        let memo_value = new_value.value.clone();
 
         debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,);
 
@@ -372,20 +348,16 @@ where
                     return ProbeState::Stale(state);
                 }
 
-                if let Some(value) = &memo.value {
-                    let value = StampedValue {
-                        durability: memo.revisions.durability,
-                        changed_at: memo.revisions.changed_at,
-                        value: value.clone(),
-                    };
+                let value = &memo.value;
+                let value = StampedValue {
+                    durability: memo.revisions.durability,
+                    changed_at: memo.revisions.changed_at,
+                    value: value.clone(),
+                };
 
-                    info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at);
+                info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at);
 
-                    ProbeState::UpToDate(value)
-                } else {
-                    let changed_at = memo.revisions.changed_at;
-                    ProbeState::NoValue(state, changed_at)
-                }
+                ProbeState::UpToDate(value)
             }
         }
     }
@@ -408,21 +380,9 @@ where
         match &*self.state.read() {
             QueryState::NotComputed => None,
             QueryState::InProgress { .. } => Some(TableEntry::new(key.clone(), None)),
-            QueryState::Memoized(memo) => Some(TableEntry::new(key.clone(), memo.value.clone())),
-        }
-    }
-
-    pub(super) fn evict(&self) {
-        let mut state = self.state.write();
-        if let QueryState::Memoized(memo) = &mut *state {
-            // Evicting a value with an untracked input could
-            // lead to inconsistencies. Note that we can't check
-            // `has_untracked_input` when we add the value to the cache,
-            // because inputs can become untracked in the next revision.
-            if memo.has_untracked_input() {
-                return;
+            QueryState::Memoized(memo) => {
+                Some(TableEntry::new(key.clone(), Some(memo.value.clone())))
             }
-            memo.value = None;
         }
     }
 
@@ -430,7 +390,8 @@ where
         tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision);
         match &mut *self.state.write() {
             QueryState::Memoized(memo) => {
-                memo.revisions.inputs = QueryInputs::Untracked;
+                memo.revisions.untracked = true;
+                memo.revisions.inputs = None;
                 memo.revisions.changed_at = new_revision;
                 Some(memo.revisions.durability)
             }
@@ -489,8 +450,7 @@ where
 
             // If we know when value last changed, we can return right away.
             // Note that we don't need the actual value to be available.
-            ProbeState::NoValue(_, changed_at)
-            | ProbeState::UpToDate(StampedValue { value: _, durability: _, changed_at }) => {
+            ProbeState::UpToDate(StampedValue { value: _, durability: _, changed_at }) => {
                 MaybeChangedSinceProbeState::ChangedAt(changed_at)
             }
 
@@ -545,7 +505,7 @@ where
             let maybe_changed = old_memo.revisions.changed_at > revision;
             panic_guard.proceed(Some(old_memo));
             maybe_changed
-        } else if old_memo.value.is_some() {
+        } else {
             // We found that this memoized value may have changed
             // but we have an old value. We can re-run the code and
             // actually *check* if it has changed.
@@ -559,12 +519,6 @@ where
                 key,
             );
             changed_at > revision
-        } else {
-            // We found that inputs to this memoized value may have chanced
-            // but we don't have an old value to compare against or re-use.
-            // No choice but to drop the memo and say that its value may have changed.
-            panic_guard.proceed(None);
-            true
         }
     }
 
@@ -583,10 +537,6 @@ where
             mutex_guard,
         )
     }
-
-    fn should_memoize_value(&self, key: &Q::Key) -> bool {
-        MP::should_memoize_value(key)
-    }
 }
 
 impl QueryState
@@ -598,21 +548,21 @@ where
     }
 }
 
-struct PanicGuard<'me, Q, MP>
+struct PanicGuard<'me, Q>
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+    Q::Value: Eq,
 {
-    slot: &'me Slot,
+    slot: &'me Slot,
     runtime: &'me Runtime,
 }
 
-impl<'me, Q, MP> PanicGuard<'me, Q, MP>
+impl<'me, Q> PanicGuard<'me, Q>
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+    Q::Value: Eq,
 {
-    fn new(slot: &'me Slot, runtime: &'me Runtime) -> Self {
+    fn new(slot: &'me Slot, runtime: &'me Runtime) -> Self {
         Self { slot, runtime }
     }
 
@@ -666,10 +616,10 @@ Please report this bug to https://github.com/salsa-rs/salsa/issues."
     }
 }
 
-impl<'me, Q, MP> Drop for PanicGuard<'me, Q, MP>
+impl<'me, Q> Drop for PanicGuard<'me, Q>
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+    Q::Value: Eq,
 {
     fn drop(&mut self) {
         if std::thread::panicking() {
@@ -702,15 +652,11 @@ where
         revision_now: Revision,
         active_query: &ActiveQueryGuard<'_>,
     ) -> Option> {
-        // If we don't have a memoized value, nothing to validate.
-        if self.value.is_none() {
-            return None;
-        }
         if self.verify_revisions(db, revision_now, active_query) {
-            self.value.clone().map(|value| StampedValue {
+            Some(StampedValue {
                 durability: self.revisions.durability,
                 changed_at: self.revisions.changed_at,
-                value,
+                value: self.value.clone(),
             })
         } else {
             None
@@ -746,11 +692,8 @@ where
         match &self.revisions.inputs {
             // We can't validate values that had untracked inputs; just have to
             // re-execute.
-            QueryInputs::Untracked => {
-                return false;
-            }
-
-            QueryInputs::NoInputs => {}
+            None if self.revisions.untracked => return false,
+            None => {}
 
             // Check whether any of our inputs changed since the
             // **last point where we were verified** (not since we
@@ -761,7 +704,7 @@ where
             // R1. But our *verification* date will be R2, and we
             // are only interested in finding out whether the
             // input changed *again*.
-            QueryInputs::Tracked { inputs } => {
+            Some(inputs) => {
                 let changed_input =
                     inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at));
                 if let Some(input) = changed_input {
@@ -791,58 +734,42 @@ where
         self.verified_at = revision_now;
         true
     }
-
-    fn has_untracked_input(&self) -> bool {
-        matches!(self.revisions.inputs, QueryInputs::Untracked)
-    }
 }
 
-impl std::fmt::Debug for Slot
+impl std::fmt::Debug for Slot
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
 {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(fmt, "{:?}", Q::default())
     }
 }
 
-impl LruNode for Slot
-where
-    Q: QueryFunction,
-    MP: MemoizationPolicy,
-{
-    fn lru_index(&self) -> &LruIndex {
-        &self.lru_index
-    }
-}
-
-/// Check that `Slot: Send + Sync` as long as
+/// Check that `Slot: Send + Sync` as long as
 /// `DB::DatabaseData: Send + Sync`, which in turn implies that
 /// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`.
 #[allow(dead_code)]
-fn check_send_sync()
+fn check_send_sync()
 where
     Q: QueryFunction,
-    MP: MemoizationPolicy,
+
     Q::Key: Send + Sync,
     Q::Value: Send + Sync,
 {
     fn is_send_sync() {}
-    is_send_sync::>();
+    is_send_sync::>();
 }
 
-/// Check that `Slot: 'static` as long as
+/// Check that `Slot: 'static` as long as
 /// `DB::DatabaseData: 'static`, which in turn implies that
 /// `Q::Key: 'static`, `Q::Value: 'static`.
 #[allow(dead_code)]
-fn check_static()
+fn check_static()
 where
     Q: QueryFunction + 'static,
-    MP: MemoizationPolicy + 'static,
     Q::Key: 'static,
     Q::Value: 'static,
 {
     fn is_static() {}
-    is_static::>();
+    is_static::>();
 }
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs b/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs
new file mode 100644
index 0000000000000..bdb448e2412ee
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs
@@ -0,0 +1,233 @@
+use crate::debug::TableEntry;
+use crate::durability::Durability;
+use crate::hash::FxIndexMap;
+use crate::lru::Lru;
+use crate::plumbing::DerivedQueryStorageOps;
+use crate::plumbing::LruQueryStorageOps;
+use crate::plumbing::QueryFunction;
+use crate::plumbing::QueryStorageMassOps;
+use crate::plumbing::QueryStorageOps;
+use crate::runtime::StampedValue;
+use crate::Runtime;
+use crate::{Database, DatabaseKeyIndex, QueryDb, Revision};
+use parking_lot::RwLock;
+use std::borrow::Borrow;
+use std::hash::Hash;
+use std::marker::PhantomData;
+use triomphe::Arc;
+
+mod slot;
+use slot::Slot;
+
+/// Memoized queries store the result plus a list of the other queries
+/// that they invoked. This means we can avoid recomputing them when
+/// none of those inputs have changed.
+pub type MemoizedStorage = DerivedStorage;
+
+/// "Dependency" queries just track their dependencies and not the
+/// actual value (which they produce on demand). This lessens the
+/// storage requirements.
+pub type DependencyStorage = DerivedStorage;
+
+/// Handles storage where the value is 'derived' by executing a
+/// function (in contrast to "inputs").
+pub struct DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    group_index: u16,
+    lru_list: Lru>,
+    slot_map: RwLock>>>,
+    policy: PhantomData,
+}
+
+impl std::panic::RefUnwindSafe for DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+    Q::Key: std::panic::RefUnwindSafe,
+    Q::Value: std::panic::RefUnwindSafe,
+{
+}
+
+pub trait MemoizationPolicy: Send + Sync
+where
+    Q: QueryFunction,
+{
+    fn should_memoize_value(key: &Q::Key) -> bool;
+
+    fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool;
+}
+
+pub enum AlwaysMemoizeValue {}
+impl MemoizationPolicy for AlwaysMemoizeValue
+where
+    Q: QueryFunction,
+    Q::Value: Eq,
+{
+    fn should_memoize_value(_key: &Q::Key) -> bool {
+        true
+    }
+
+    fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool {
+        old_value == new_value
+    }
+}
+
+pub enum NeverMemoizeValue {}
+impl MemoizationPolicy for NeverMemoizeValue
+where
+    Q: QueryFunction,
+{
+    fn should_memoize_value(_key: &Q::Key) -> bool {
+        false
+    }
+
+    fn memoized_value_eq(_old_value: &Q::Value, _new_value: &Q::Value) -> bool {
+        panic!("cannot reach since we never memoize")
+    }
+}
+
+impl DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn slot(&self, key: &Q::Key) -> Arc> {
+        if let Some(v) = self.slot_map.read().get(key) {
+            return v.clone();
+        }
+
+        let mut write = self.slot_map.write();
+        let entry = write.entry(key.clone());
+        let key_index = entry.index() as u32;
+        let database_key_index = DatabaseKeyIndex {
+            group_index: self.group_index,
+            query_index: Q::QUERY_INDEX,
+            key_index,
+        };
+        entry.or_insert_with(|| Arc::new(Slot::new(database_key_index))).clone()
+    }
+}
+
+impl QueryStorageOps for DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    const CYCLE_STRATEGY: crate::plumbing::CycleRecoveryStrategy = Q::CYCLE_STRATEGY;
+
+    fn new(group_index: u16) -> Self {
+        DerivedStorage {
+            group_index,
+            slot_map: RwLock::new(FxIndexMap::default()),
+            lru_list: Default::default(),
+            policy: PhantomData,
+        }
+    }
+
+    fn fmt_index(
+        &self,
+        _db: &>::DynDb,
+        index: u32,
+        fmt: &mut std::fmt::Formatter<'_>,
+    ) -> std::fmt::Result {
+        let slot_map = self.slot_map.read();
+        let key = slot_map.get_index(index as usize).unwrap().0;
+        write!(fmt, "{}::{}({:?})", std::any::type_name::(), Q::QUERY_NAME, key)
+    }
+
+    fn maybe_changed_after(
+        &self,
+        db: &>::DynDb,
+        index: u32,
+        revision: Revision,
+    ) -> bool {
+        debug_assert!(revision < db.salsa_runtime().current_revision());
+        let (key, slot) = {
+            let read = self.slot_map.read();
+            let Some((key, slot)) = read.get_index(index as usize) else {
+                return false;
+            };
+            (key.clone(), slot.clone())
+        };
+        slot.maybe_changed_after(db, revision, &key)
+    }
+
+    fn fetch(&self, db: &>::DynDb, key: &Q::Key) -> Q::Value {
+        db.unwind_if_cancelled();
+
+        let slot = self.slot(key);
+        let StampedValue { value, durability, changed_at } = slot.read(db, key);
+
+        if let Some(evicted) = self.lru_list.record_use(&slot) {
+            evicted.evict();
+        }
+
+        db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted(
+            slot.database_key_index(),
+            durability,
+            changed_at,
+        );
+
+        value
+    }
+
+    fn durability(&self, db: &>::DynDb, key: &Q::Key) -> Durability {
+        self.slot(key).durability(db)
+    }
+
+    fn entries(&self, _db: &>::DynDb) -> C
+    where
+        C: std::iter::FromIterator>,
+    {
+        let slot_map = self.slot_map.read();
+        slot_map.iter().filter_map(|(key, slot)| slot.as_table_entry(key)).collect()
+    }
+}
+
+impl QueryStorageMassOps for DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn purge(&self) {
+        self.lru_list.purge();
+        *self.slot_map.write() = Default::default();
+    }
+}
+
+impl LruQueryStorageOps for DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn set_lru_capacity(&self, new_capacity: u16) {
+        self.lru_list.set_lru_capacity(new_capacity);
+    }
+}
+
+impl DerivedQueryStorageOps for DerivedStorage
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn invalidate(&self, runtime: &mut Runtime, key: &S)
+    where
+        S: Eq + Hash,
+        Q::Key: Borrow,
+    {
+        runtime.with_incremented_revision(|new_revision| {
+            let map_read = self.slot_map.read();
+
+            if let Some(slot) = map_read.get(key) {
+                if let Some(durability) = slot.invalidate(new_revision) {
+                    return Some(durability);
+                }
+            }
+
+            None
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs b/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs
new file mode 100644
index 0000000000000..d0e4b5422b5f5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs
@@ -0,0 +1,845 @@
+use crate::debug::TableEntry;
+use crate::derived_lru::MemoizationPolicy;
+use crate::durability::Durability;
+use crate::lru::LruIndex;
+use crate::lru::LruNode;
+use crate::plumbing::{DatabaseOps, QueryFunction};
+use crate::revision::Revision;
+use crate::runtime::local_state::ActiveQueryGuard;
+use crate::runtime::local_state::QueryRevisions;
+use crate::runtime::Runtime;
+use crate::runtime::RuntimeId;
+use crate::runtime::StampedValue;
+use crate::runtime::WaitResult;
+use crate::Cycle;
+use crate::{Database, DatabaseKeyIndex, Event, EventKind, QueryDb};
+use parking_lot::{RawRwLock, RwLock};
+use std::marker::PhantomData;
+use std::ops::Deref;
+use std::sync::atomic::{AtomicBool, Ordering};
+use tracing::{debug, info};
+
+pub(super) struct Slot
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    key_index: u32,
+    group_index: u16,
+    state: RwLock>,
+    lru_index: LruIndex,
+    policy: PhantomData,
+}
+
+/// Defines the "current state" of query's memoized results.
+enum QueryState
+where
+    Q: QueryFunction,
+{
+    NotComputed,
+
+    /// The runtime with the given id is currently computing the
+    /// result of this query.
+    InProgress {
+        id: RuntimeId,
+
+        /// Set to true if any other queries are blocked,
+        /// waiting for this query to complete.
+        anyone_waiting: AtomicBool,
+    },
+
+    /// We have computed the query already, and here is the result.
+    Memoized(Memo),
+}
+
+struct Memo {
+    /// The result of the query, if we decide to memoize it.
+    value: Option,
+
+    /// Last revision when this memo was verified; this begins
+    /// as the current revision.
+    pub(crate) verified_at: Revision,
+
+    /// Revision information
+    revisions: QueryRevisions,
+}
+
+/// Return value of `probe` helper.
+enum ProbeState {
+    /// Another thread was active but has completed.
+    /// Try again!
+    Retry,
+
+    /// No entry for this key at all.
+    NotComputed(G),
+
+    /// There is an entry, but its contents have not been
+    /// verified in this revision.
+    Stale(G),
+
+    /// There is an entry, and it has been verified
+    /// in this revision, but it has no cached
+    /// value. The `Revision` is the revision where the
+    /// value last changed (if we were to recompute it).
+    NoValue(G, Revision),
+
+    /// There is an entry which has been verified,
+    /// and it has the following value-- or, we blocked
+    /// on another thread, and that resulted in a cycle.
+    UpToDate(V),
+}
+
+/// Return value of `maybe_changed_after_probe` helper.
+enum MaybeChangedSinceProbeState {
+    /// Another thread was active but has completed.
+    /// Try again!
+    Retry,
+
+    /// Value may have changed in the given revision.
+    ChangedAt(Revision),
+
+    /// There is a stale cache entry that has not been
+    /// verified in this revision, so we can't say.
+    Stale(G),
+}
+
+impl Slot
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    pub(super) fn new(database_key_index: DatabaseKeyIndex) -> Self {
+        Self {
+            key_index: database_key_index.key_index,
+            group_index: database_key_index.group_index,
+            state: RwLock::new(QueryState::NotComputed),
+            lru_index: LruIndex::default(),
+            policy: PhantomData,
+        }
+    }
+
+    pub(super) fn database_key_index(&self) -> DatabaseKeyIndex {
+        DatabaseKeyIndex {
+            group_index: self.group_index,
+            query_index: Q::QUERY_INDEX,
+            key_index: self.key_index,
+        }
+    }
+
+    pub(super) fn read(
+        &self,
+        db: &>::DynDb,
+        key: &Q::Key,
+    ) -> StampedValue {
+        let runtime = db.salsa_runtime();
+
+        // NB: We don't need to worry about people modifying the
+        // revision out from under our feet. Either `db` is a frozen
+        // database, in which case there is a lock, or the mutator
+        // thread is the current thread, and it will be prevented from
+        // doing any `set` invocations while the query function runs.
+        let revision_now = runtime.current_revision();
+
+        info!("{:?}: invoked at {:?}", self, revision_now,);
+
+        // First, do a check with a read-lock.
+        loop {
+            match self.probe(db, self.state.read(), runtime, revision_now) {
+                ProbeState::UpToDate(v) => return v,
+                ProbeState::Stale(..) | ProbeState::NoValue(..) | ProbeState::NotComputed(..) => {
+                    break
+                }
+                ProbeState::Retry => continue,
+            }
+        }
+
+        self.read_upgrade(db, key, revision_now)
+    }
+
+    /// Second phase of a read operation: acquires an upgradable-read
+    /// and -- if needed -- validates whether inputs have changed,
+    /// recomputes value, etc. This is invoked after our initial probe
+    /// shows a potentially out of date value.
+    fn read_upgrade(
+        &self,
+        db: &>::DynDb,
+        key: &Q::Key,
+        revision_now: Revision,
+    ) -> StampedValue {
+        let runtime = db.salsa_runtime();
+
+        debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,);
+
+        // Check with an upgradable read to see if there is a value
+        // already. (This permits other readers but prevents anyone
+        // else from running `read_upgrade` at the same time.)
+        let mut old_memo = loop {
+            match self.probe(db, self.state.upgradable_read(), runtime, revision_now) {
+                ProbeState::UpToDate(v) => return v,
+                ProbeState::Stale(state)
+                | ProbeState::NotComputed(state)
+                | ProbeState::NoValue(state, _) => {
+                    type RwLockUpgradableReadGuard<'a, T> =
+                        lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>;
+
+                    let mut state = RwLockUpgradableReadGuard::upgrade(state);
+                    match std::mem::replace(&mut *state, QueryState::in_progress(runtime.id())) {
+                        QueryState::Memoized(old_memo) => break Some(old_memo),
+                        QueryState::InProgress { .. } => unreachable!(),
+                        QueryState::NotComputed => break None,
+                    }
+                }
+                ProbeState::Retry => continue,
+            }
+        };
+
+        let panic_guard = PanicGuard::new(self, runtime);
+        let active_query = runtime.push_query(self.database_key_index());
+
+        // If we have an old-value, it *may* now be stale, since there
+        // has been a new revision since the last time we checked. So,
+        // first things first, let's walk over each of our previous
+        // inputs and check whether they are out of date.
+        if let Some(memo) = &mut old_memo {
+            if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) {
+                info!("{:?}: validated old memoized value", self,);
+
+                db.salsa_event(Event {
+                    runtime_id: runtime.id(),
+                    kind: EventKind::DidValidateMemoizedValue {
+                        database_key: self.database_key_index(),
+                    },
+                });
+
+                panic_guard.proceed(old_memo);
+
+                return value;
+            }
+        }
+
+        self.execute(db, runtime, revision_now, active_query, panic_guard, old_memo, key)
+    }
+
+    fn execute(
+        &self,
+        db: &>::DynDb,
+        runtime: &Runtime,
+        revision_now: Revision,
+        active_query: ActiveQueryGuard<'_>,
+        panic_guard: PanicGuard<'_, Q, MP>,
+        old_memo: Option>,
+        key: &Q::Key,
+    ) -> StampedValue {
+        tracing::info!("{:?}: executing query", self.database_key_index().debug(db));
+
+        db.salsa_event(Event {
+            runtime_id: db.salsa_runtime().id(),
+            kind: EventKind::WillExecute { database_key: self.database_key_index() },
+        });
+
+        // Query was not previously executed, or value is potentially
+        // stale, or value is absent. Let's execute!
+        let value = match Cycle::catch(|| Q::execute(db, key.clone())) {
+            Ok(v) => v,
+            Err(cycle) => {
+                tracing::debug!(
+                    "{:?}: caught cycle {:?}, have strategy {:?}",
+                    self.database_key_index().debug(db),
+                    cycle,
+                    Q::CYCLE_STRATEGY,
+                );
+                match Q::CYCLE_STRATEGY {
+                    crate::plumbing::CycleRecoveryStrategy::Panic => {
+                        panic_guard.proceed(None);
+                        cycle.throw()
+                    }
+                    crate::plumbing::CycleRecoveryStrategy::Fallback => {
+                        if let Some(c) = active_query.take_cycle() {
+                            assert!(c.is(&cycle));
+                            Q::cycle_fallback(db, &cycle, key)
+                        } else {
+                            // we are not a participant in this cycle
+                            debug_assert!(!cycle
+                                .participant_keys()
+                                .any(|k| k == self.database_key_index()));
+                            cycle.throw()
+                        }
+                    }
+                }
+            }
+        };
+
+        let mut revisions = active_query.pop();
+
+        // We assume that query is side-effect free -- that is, does
+        // not mutate the "inputs" to the query system. Sanity check
+        // that assumption here, at least to the best of our ability.
+        assert_eq!(
+            runtime.current_revision(),
+            revision_now,
+            "revision altered during query execution",
+        );
+
+        // If the new value is equal to the old one, then it didn't
+        // really change, even if some of its inputs have. So we can
+        // "backdate" its `changed_at` revision to be the same as the
+        // old value.
+        if let Some(old_memo) = &old_memo {
+            if let Some(old_value) = &old_memo.value {
+                // Careful: if the value became less durable than it
+                // used to be, that is a "breaking change" that our
+                // consumers must be aware of. Becoming *more* durable
+                // is not. See the test `constant_to_non_constant`.
+                if revisions.durability >= old_memo.revisions.durability
+                    && MP::memoized_value_eq(old_value, &value)
+                {
+                    debug!(
+                        "read_upgrade({:?}): value is equal, back-dating to {:?}",
+                        self, old_memo.revisions.changed_at,
+                    );
+
+                    assert!(old_memo.revisions.changed_at <= revisions.changed_at);
+                    revisions.changed_at = old_memo.revisions.changed_at;
+                }
+            }
+        }
+
+        let new_value = StampedValue {
+            value,
+            durability: revisions.durability,
+            changed_at: revisions.changed_at,
+        };
+
+        let memo_value =
+            if self.should_memoize_value(key) { Some(new_value.value.clone()) } else { None };
+
+        debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,);
+
+        panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions }));
+
+        new_value
+    }
+
+    /// Helper for `read` that does a shallow check (not recursive) if we have an up-to-date value.
+    ///
+    /// Invoked with the guard `state` corresponding to the `QueryState` of some `Slot` (the guard
+    /// can be either read or write). Returns a suitable `ProbeState`:
+    ///
+    /// - `ProbeState::UpToDate(r)` if the table has an up-to-date value (or we blocked on another
+    ///   thread that produced such a value).
+    /// - `ProbeState::StaleOrAbsent(g)` if either (a) there is no memo for this key, (b) the memo
+    ///   has no value; or (c) the memo has not been verified at the current revision.
+    ///
+    /// Note that in case `ProbeState::UpToDate`, the lock will have been released.
+    fn probe(
+        &self,
+        db: &>::DynDb,
+        state: StateGuard,
+        runtime: &Runtime,
+        revision_now: Revision,
+    ) -> ProbeState, StateGuard>
+    where
+        StateGuard: Deref>,
+    {
+        match &*state {
+            QueryState::NotComputed => ProbeState::NotComputed(state),
+
+            QueryState::InProgress { id, anyone_waiting } => {
+                let other_id = *id;
+
+                // NB: `Ordering::Relaxed` is sufficient here,
+                // as there are no loads that are "gated" on this
+                // value. Everything that is written is also protected
+                // by a lock that must be acquired. The role of this
+                // boolean is to decide *whether* to acquire the lock,
+                // not to gate future atomic reads.
+                anyone_waiting.store(true, Ordering::Relaxed);
+
+                self.block_on_or_unwind(db, runtime, other_id, state);
+
+                // Other thread completely normally, so our value may be available now.
+                ProbeState::Retry
+            }
+
+            QueryState::Memoized(memo) => {
+                debug!(
+                    "{:?}: found memoized value, verified_at={:?}, changed_at={:?}",
+                    self, memo.verified_at, memo.revisions.changed_at,
+                );
+
+                if memo.verified_at < revision_now {
+                    return ProbeState::Stale(state);
+                }
+
+                if let Some(value) = &memo.value {
+                    let value = StampedValue {
+                        durability: memo.revisions.durability,
+                        changed_at: memo.revisions.changed_at,
+                        value: value.clone(),
+                    };
+
+                    info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at);
+
+                    ProbeState::UpToDate(value)
+                } else {
+                    let changed_at = memo.revisions.changed_at;
+                    ProbeState::NoValue(state, changed_at)
+                }
+            }
+        }
+    }
+
+    pub(super) fn durability(&self, db: &>::DynDb) -> Durability {
+        match &*self.state.read() {
+            QueryState::NotComputed => Durability::LOW,
+            QueryState::InProgress { .. } => panic!("query in progress"),
+            QueryState::Memoized(memo) => {
+                if memo.check_durability(db.salsa_runtime()) {
+                    memo.revisions.durability
+                } else {
+                    Durability::LOW
+                }
+            }
+        }
+    }
+
+    pub(super) fn as_table_entry(&self, key: &Q::Key) -> Option> {
+        match &*self.state.read() {
+            QueryState::NotComputed => None,
+            QueryState::InProgress { .. } => Some(TableEntry::new(key.clone(), None)),
+            QueryState::Memoized(memo) => Some(TableEntry::new(key.clone(), memo.value.clone())),
+        }
+    }
+
+    pub(super) fn evict(&self) {
+        let mut state = self.state.write();
+        if let QueryState::Memoized(memo) = &mut *state {
+            // Evicting a value with an untracked input could
+            // lead to inconsistencies. Note that we can't check
+            // `has_untracked_input` when we add the value to the cache,
+            // because inputs can become untracked in the next revision.
+            if memo.has_untracked_input() {
+                return;
+            }
+            memo.value = None;
+        }
+    }
+
+    pub(super) fn invalidate(&self, new_revision: Revision) -> Option {
+        tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision);
+        match &mut *self.state.write() {
+            QueryState::Memoized(memo) => {
+                memo.revisions.untracked = true;
+                memo.revisions.inputs = None;
+                memo.revisions.changed_at = new_revision;
+                Some(memo.revisions.durability)
+            }
+            QueryState::NotComputed => None,
+            QueryState::InProgress { .. } => unreachable!(),
+        }
+    }
+
+    pub(super) fn maybe_changed_after(
+        &self,
+        db: &>::DynDb,
+        revision: Revision,
+        key: &Q::Key,
+    ) -> bool {
+        let runtime = db.salsa_runtime();
+        let revision_now = runtime.current_revision();
+
+        db.unwind_if_cancelled();
+
+        debug!(
+            "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}",
+            self, revision, revision_now,
+        );
+
+        // Do an initial probe with just the read-lock.
+        //
+        // If we find that a cache entry for the value is present
+        // but hasn't been verified in this revision, we'll have to
+        // do more.
+        loop {
+            match self.maybe_changed_after_probe(db, self.state.read(), runtime, revision_now) {
+                MaybeChangedSinceProbeState::Retry => continue,
+                MaybeChangedSinceProbeState::ChangedAt(changed_at) => return changed_at > revision,
+                MaybeChangedSinceProbeState::Stale(state) => {
+                    drop(state);
+                    return self.maybe_changed_after_upgrade(db, revision, key);
+                }
+            }
+        }
+    }
+
+    fn maybe_changed_after_probe(
+        &self,
+        db: &>::DynDb,
+        state: StateGuard,
+        runtime: &Runtime,
+        revision_now: Revision,
+    ) -> MaybeChangedSinceProbeState
+    where
+        StateGuard: Deref>,
+    {
+        match self.probe(db, state, runtime, revision_now) {
+            ProbeState::Retry => MaybeChangedSinceProbeState::Retry,
+
+            ProbeState::Stale(state) => MaybeChangedSinceProbeState::Stale(state),
+
+            // If we know when value last changed, we can return right away.
+            // Note that we don't need the actual value to be available.
+            ProbeState::NoValue(_, changed_at)
+            | ProbeState::UpToDate(StampedValue { value: _, durability: _, changed_at }) => {
+                MaybeChangedSinceProbeState::ChangedAt(changed_at)
+            }
+
+            // If we have nothing cached, then value may have changed.
+            ProbeState::NotComputed(_) => MaybeChangedSinceProbeState::ChangedAt(revision_now),
+        }
+    }
+
+    fn maybe_changed_after_upgrade(
+        &self,
+        db: &>::DynDb,
+        revision: Revision,
+        key: &Q::Key,
+    ) -> bool {
+        let runtime = db.salsa_runtime();
+        let revision_now = runtime.current_revision();
+
+        // Get an upgradable read lock, which permits other reads but no writers.
+        // Probe again. If the value is stale (needs to be verified), then upgrade
+        // to a write lock and swap it with InProgress while we work.
+        let mut old_memo = match self.maybe_changed_after_probe(
+            db,
+            self.state.upgradable_read(),
+            runtime,
+            revision_now,
+        ) {
+            MaybeChangedSinceProbeState::ChangedAt(changed_at) => return changed_at > revision,
+
+            // If another thread was active, then the cache line is going to be
+            // either verified or cleared out. Just recurse to figure out which.
+            // Note that we don't need an upgradable read.
+            MaybeChangedSinceProbeState::Retry => {
+                return self.maybe_changed_after(db, revision, key)
+            }
+
+            MaybeChangedSinceProbeState::Stale(state) => {
+                type RwLockUpgradableReadGuard<'a, T> =
+                    lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>;
+
+                let mut state = RwLockUpgradableReadGuard::upgrade(state);
+                match std::mem::replace(&mut *state, QueryState::in_progress(runtime.id())) {
+                    QueryState::Memoized(old_memo) => old_memo,
+                    QueryState::NotComputed | QueryState::InProgress { .. } => unreachable!(),
+                }
+            }
+        };
+
+        let panic_guard = PanicGuard::new(self, runtime);
+        let active_query = runtime.push_query(self.database_key_index());
+
+        if old_memo.verify_revisions(db.ops_database(), revision_now, &active_query) {
+            let maybe_changed = old_memo.revisions.changed_at > revision;
+            panic_guard.proceed(Some(old_memo));
+            maybe_changed
+        } else if old_memo.value.is_some() {
+            // We found that this memoized value may have changed
+            // but we have an old value. We can re-run the code and
+            // actually *check* if it has changed.
+            let StampedValue { changed_at, .. } = self.execute(
+                db,
+                runtime,
+                revision_now,
+                active_query,
+                panic_guard,
+                Some(old_memo),
+                key,
+            );
+            changed_at > revision
+        } else {
+            // We found that inputs to this memoized value may have chanced
+            // but we don't have an old value to compare against or re-use.
+            // No choice but to drop the memo and say that its value may have changed.
+            panic_guard.proceed(None);
+            true
+        }
+    }
+
+    /// Helper: see [`Runtime::try_block_on_or_unwind`].
+    fn block_on_or_unwind(
+        &self,
+        db: &>::DynDb,
+        runtime: &Runtime,
+        other_id: RuntimeId,
+        mutex_guard: MutexGuard,
+    ) {
+        runtime.block_on_or_unwind(
+            db.ops_database(),
+            self.database_key_index(),
+            other_id,
+            mutex_guard,
+        )
+    }
+
+    fn should_memoize_value(&self, key: &Q::Key) -> bool {
+        MP::should_memoize_value(key)
+    }
+}
+
+impl QueryState
+where
+    Q: QueryFunction,
+{
+    fn in_progress(id: RuntimeId) -> Self {
+        QueryState::InProgress { id, anyone_waiting: Default::default() }
+    }
+}
+
+struct PanicGuard<'me, Q, MP>
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    slot: &'me Slot,
+    runtime: &'me Runtime,
+}
+
+impl<'me, Q, MP> PanicGuard<'me, Q, MP>
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn new(slot: &'me Slot, runtime: &'me Runtime) -> Self {
+        Self { slot, runtime }
+    }
+
+    /// Indicates that we have concluded normally (without panicking).
+    /// If `opt_memo` is some, then this memo is installed as the new
+    /// memoized value. If `opt_memo` is `None`, then the slot is cleared
+    /// and has no value.
+    fn proceed(mut self, opt_memo: Option>) {
+        self.overwrite_placeholder(WaitResult::Completed, opt_memo);
+        std::mem::forget(self)
+    }
+
+    /// Overwrites the `InProgress` placeholder for `key` that we
+    /// inserted; if others were blocked, waiting for us to finish,
+    /// then notify them.
+    fn overwrite_placeholder(&mut self, wait_result: WaitResult, opt_memo: Option>) {
+        let old_value = {
+            let mut write = self.slot.state.write();
+            match opt_memo {
+                // Replace the `InProgress` marker that we installed with the new
+                // memo, thus releasing our unique access to this key.
+                Some(memo) => std::mem::replace(&mut *write, QueryState::Memoized(memo)),
+
+                // We had installed an `InProgress` marker, but we panicked before
+                // it could be removed. At this point, we therefore "own" unique
+                // access to our slot, so we can just remove the key.
+                None => std::mem::replace(&mut *write, QueryState::NotComputed),
+            }
+        };
+
+        match old_value {
+            QueryState::InProgress { id, anyone_waiting } => {
+                assert_eq!(id, self.runtime.id());
+
+                // NB: As noted on the `store`, `Ordering::Relaxed` is
+                // sufficient here. This boolean signals us on whether to
+                // acquire a mutex; the mutex will guarantee that all writes
+                // we are interested in are visible.
+                if anyone_waiting.load(Ordering::Relaxed) {
+                    self.runtime
+                        .unblock_queries_blocked_on(self.slot.database_key_index(), wait_result);
+                }
+            }
+            _ => panic!(
+                "\
+Unexpected panic during query evaluation, aborting the process.
+
+Please report this bug to https://github.com/salsa-rs/salsa/issues."
+            ),
+        }
+    }
+}
+
+impl<'me, Q, MP> Drop for PanicGuard<'me, Q, MP>
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn drop(&mut self) {
+        if std::thread::panicking() {
+            // We panicked before we could proceed and need to remove `key`.
+            self.overwrite_placeholder(WaitResult::Panicked, None)
+        } else {
+            // If no panic occurred, then panic guard ought to be
+            // "forgotten" and so this Drop code should never run.
+            panic!(".forget() was not called")
+        }
+    }
+}
+
+impl Memo
+where
+    V: Clone,
+{
+    /// Determines whether the value stored in this memo (if any) is still
+    /// valid in the current revision. If so, returns a stamped value.
+    ///
+    /// If needed, this will walk each dependency and
+    /// recursively invoke `maybe_changed_after`, which may in turn
+    /// re-execute the dependency. This can cause cycles to occur,
+    /// so the current query must be pushed onto the
+    /// stack to permit cycle detection and recovery: therefore,
+    /// takes the `active_query` argument as evidence.
+    fn verify_value(
+        &mut self,
+        db: &dyn Database,
+        revision_now: Revision,
+        active_query: &ActiveQueryGuard<'_>,
+    ) -> Option> {
+        // If we don't have a memoized value, nothing to validate.
+        if self.value.is_none() {
+            return None;
+        }
+        if self.verify_revisions(db, revision_now, active_query) {
+            self.value.clone().map(|value| StampedValue {
+                durability: self.revisions.durability,
+                changed_at: self.revisions.changed_at,
+                value,
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Determines whether the value represented by this memo is still
+    /// valid in the current revision; note that the value itself is
+    /// not needed for this check. If needed, this will walk each
+    /// dependency and recursively invoke `maybe_changed_after`, which
+    /// may in turn re-execute the dependency. This can cause cycles to occur,
+    /// so the current query must be pushed onto the
+    /// stack to permit cycle detection and recovery: therefore,
+    /// takes the `active_query` argument as evidence.
+    fn verify_revisions(
+        &mut self,
+        db: &dyn Database,
+        revision_now: Revision,
+        _active_query: &ActiveQueryGuard<'_>,
+    ) -> bool {
+        assert!(self.verified_at != revision_now);
+        let verified_at = self.verified_at;
+
+        debug!(
+            "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}",
+            verified_at, revision_now, self.revisions.inputs
+        );
+
+        if self.check_durability(db.salsa_runtime()) {
+            return self.mark_value_as_verified(revision_now);
+        }
+
+        match &self.revisions.inputs {
+            // We can't validate values that had untracked inputs; just have to
+            // re-execute.
+            None if self.revisions.untracked => return false,
+            None => {}
+
+            // Check whether any of our inputs changed since the
+            // **last point where we were verified** (not since we
+            // last changed). This is important: if we have
+            // memoized values, then an input may have changed in
+            // revision R2, but we found that *our* value was the
+            // same regardless, so our change date is still
+            // R1. But our *verification* date will be R2, and we
+            // are only interested in finding out whether the
+            // input changed *again*.
+            Some(inputs) => {
+                let changed_input =
+                    inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at));
+                if let Some(input) = changed_input {
+                    debug!("validate_memoized_value: `{:?}` may have changed", input);
+
+                    return false;
+                }
+            }
+        };
+
+        self.mark_value_as_verified(revision_now)
+    }
+
+    /// True if this memo is known not to have changed based on its durability.
+    fn check_durability(&self, runtime: &Runtime) -> bool {
+        let last_changed = runtime.last_changed_revision(self.revisions.durability);
+        debug!(
+            "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}",
+            last_changed,
+            self.verified_at,
+            last_changed <= self.verified_at,
+        );
+        last_changed <= self.verified_at
+    }
+
+    fn mark_value_as_verified(&mut self, revision_now: Revision) -> bool {
+        self.verified_at = revision_now;
+        true
+    }
+
+    fn has_untracked_input(&self) -> bool {
+        self.revisions.untracked
+    }
+}
+
+impl std::fmt::Debug for Slot
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(fmt, "{:?}", Q::default())
+    }
+}
+
+impl LruNode for Slot
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+{
+    fn lru_index(&self) -> &LruIndex {
+        &self.lru_index
+    }
+}
+
+/// Check that `Slot: Send + Sync` as long as
+/// `DB::DatabaseData: Send + Sync`, which in turn implies that
+/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`.
+#[allow(dead_code)]
+fn check_send_sync()
+where
+    Q: QueryFunction,
+    MP: MemoizationPolicy,
+    Q::Key: Send + Sync,
+    Q::Value: Send + Sync,
+{
+    fn is_send_sync() {}
+    is_send_sync::>();
+}
+
+/// Check that `Slot: 'static` as long as
+/// `DB::DatabaseData: 'static`, which in turn implies that
+/// `Q::Key: 'static`, `Q::Value: 'static`.
+#[allow(dead_code)]
+fn check_static()
+where
+    Q: QueryFunction + 'static,
+    MP: MemoizationPolicy + 'static,
+    Q::Key: 'static,
+    Q::Value: 'static,
+{
+    fn is_static() {}
+    is_static::>();
+}
diff --git a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs b/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
index b060d8aab6881..8e74c100acab9 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs
@@ -63,7 +63,8 @@ impl InternId {
     /// `value` must be less than `MAX`
     pub const unsafe fn new_unchecked(value: u32) -> Self {
         debug_assert!(value < InternId::MAX);
-        InternId { value: NonZeroU32::new_unchecked(value + 1) }
+        let value = unsafe { NonZeroU32::new_unchecked(value + 1) };
+        InternId { value }
     }
 
     /// Convert this raw-id into a u32 value.
diff --git a/src/tools/rust-analyzer/crates/salsa/src/interned.rs b/src/tools/rust-analyzer/crates/salsa/src/interned.rs
index eef8bcc814f67..359662ec6b2fb 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/interned.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/interned.rs
@@ -121,8 +121,8 @@ impl InternValueTrivial for String {}
 
 #[derive(Debug)]
 struct Slot {
-    /// DatabaseKeyIndex for this slot.
-    database_key_index: DatabaseKeyIndex,
+    /// key index for this slot.
+    key_index: u32,
 
     /// Value that was interned.
     value: V,
@@ -199,13 +199,8 @@ where
         };
 
         let create_slot = |index: InternId| {
-            let database_key_index = DatabaseKeyIndex {
-                group_index: self.group_index,
-                query_index: Q::QUERY_INDEX,
-                key_index: index.as_u32(),
-            };
             Arc::new(Slot {
-                database_key_index,
+                key_index: index.as_u32(),
                 value: insert(Q::Value::from_intern_id(index)),
                 interned_at: revision_now,
             })
@@ -242,7 +237,11 @@ where
         };
         let changed_at = slot.interned_at;
         db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted(
-            slot.database_key_index,
+            DatabaseKeyIndex {
+                group_index: self.group_index,
+                query_index: Q::QUERY_INDEX,
+                key_index: slot.key_index,
+            },
             INTERN_DURABILITY,
             changed_at,
         );
@@ -294,7 +293,11 @@ where
         };
         let changed_at = slot.interned_at;
         db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted(
-            slot.database_key_index,
+            DatabaseKeyIndex {
+                group_index: self.group_index,
+                query_index: Q::QUERY_INDEX,
+                key_index: slot.key_index,
+            },
             INTERN_DURABILITY,
             changed_at,
         );
@@ -414,7 +417,11 @@ where
         let value = slot.value.clone();
         let interned_at = slot.interned_at;
         db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted(
-            slot.database_key_index,
+            DatabaseKeyIndex {
+                group_index: interned_storage.group_index,
+                query_index: Q::QUERY_INDEX,
+                key_index: slot.key_index,
+            },
             INTERN_DURABILITY,
             interned_at,
         );
diff --git a/src/tools/rust-analyzer/crates/salsa/src/lib.rs b/src/tools/rust-analyzer/crates/salsa/src/lib.rs
index e11e6e2e19f4c..48d6dc2e38760 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/lib.rs
@@ -10,6 +10,7 @@
 //! from previous invocations as appropriate.
 
 mod derived;
+mod derived_lru;
 mod durability;
 mod hash;
 mod input;
@@ -577,7 +578,7 @@ where
     /// cost of potential extra recalculations of evicted values.
     ///
     /// If `cap` is zero, all values are preserved, this is the default.
-    pub fn set_lru_capacity(&self, cap: usize)
+    pub fn set_lru_capacity(&self, cap: u16)
     where
         Q::Storage: plumbing::LruQueryStorageOps,
     {
diff --git a/src/tools/rust-analyzer/crates/salsa/src/lru.rs b/src/tools/rust-analyzer/crates/salsa/src/lru.rs
index f63f4c1e98638..a6f96beeab11a 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/lru.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/lru.rs
@@ -1,7 +1,7 @@
 use oorandom::Rand64;
 use parking_lot::Mutex;
 use std::fmt::Debug;
-use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::AtomicU16;
 use std::sync::atomic::Ordering;
 use triomphe::Arc;
 
@@ -20,15 +20,15 @@ pub(crate) struct Lru
 where
     Node: LruNode,
 {
-    green_zone: AtomicUsize,
+    green_zone: AtomicU16,
     data: Mutex>,
 }
 
 #[derive(Debug)]
 struct LruData {
-    end_red_zone: usize,
-    end_yellow_zone: usize,
-    end_green_zone: usize,
+    end_red_zone: u16,
+    end_yellow_zone: u16,
+    end_green_zone: u16,
     rng: Rand64,
     entries: Vec>,
 }
@@ -39,9 +39,9 @@ pub(crate) trait LruNode: Sized + Debug {
 
 #[derive(Debug)]
 pub(crate) struct LruIndex {
-    /// Index in the appropriate LRU list, or std::usize::MAX if not a
+    /// Index in the appropriate LRU list, or std::u16::MAX if not a
     /// member.
-    index: AtomicUsize,
+    index: AtomicU16,
 }
 
 impl Default for Lru
@@ -68,12 +68,12 @@ where
 
     #[cfg_attr(not(test), allow(dead_code))]
     fn with_seed(seed: &str) -> Self {
-        Lru { green_zone: AtomicUsize::new(0), data: Mutex::new(LruData::with_seed(seed)) }
+        Lru { green_zone: AtomicU16::new(0), data: Mutex::new(LruData::with_seed(seed)) }
     }
 
     /// Adjust the total number of nodes permitted to have a value at
     /// once.  If `len` is zero, this disables LRU caching completely.
-    pub(crate) fn set_lru_capacity(&self, len: usize) {
+    pub(crate) fn set_lru_capacity(&self, len: u16) {
         let mut data = self.data.lock();
 
         // We require each zone to have at least 1 slot. Therefore,
@@ -143,23 +143,24 @@ where
         LruData { end_yellow_zone: 0, end_green_zone: 0, end_red_zone: 0, entries: Vec::new(), rng }
     }
 
-    fn green_zone(&self) -> std::ops::Range {
+    fn green_zone(&self) -> std::ops::Range {
         0..self.end_green_zone
     }
 
-    fn yellow_zone(&self) -> std::ops::Range {
+    fn yellow_zone(&self) -> std::ops::Range {
         self.end_green_zone..self.end_yellow_zone
     }
 
-    fn red_zone(&self) -> std::ops::Range {
+    fn red_zone(&self) -> std::ops::Range {
         self.end_yellow_zone..self.end_red_zone
     }
 
-    fn resize(&mut self, len_green_zone: usize, len_yellow_zone: usize, len_red_zone: usize) {
+    fn resize(&mut self, len_green_zone: u16, len_yellow_zone: u16, len_red_zone: u16) {
         self.end_green_zone = len_green_zone;
         self.end_yellow_zone = self.end_green_zone + len_yellow_zone;
         self.end_red_zone = self.end_yellow_zone + len_red_zone;
-        let entries = std::mem::replace(&mut self.entries, Vec::with_capacity(self.end_red_zone));
+        let entries =
+            std::mem::replace(&mut self.entries, Vec::with_capacity(self.end_red_zone as usize));
 
         tracing::debug!("green_zone = {:?}", self.green_zone());
         tracing::debug!("yellow_zone = {:?}", self.yellow_zone());
@@ -207,7 +208,7 @@ where
 
         // Easy case: we still have capacity. Push it, and then promote
         // it up to the appropriate zone.
-        let len = self.entries.len();
+        let len = self.entries.len() as u16;
         if len < self.end_red_zone {
             self.entries.push(node.clone());
             node.lru_index().store(len);
@@ -218,7 +219,7 @@ where
         // Harder case: no capacity. Create some by evicting somebody from red
         // zone and then promoting.
         let victim_index = self.pick_index(self.red_zone());
-        let victim_node = std::mem::replace(&mut self.entries[victim_index], node.clone());
+        let victim_node = std::mem::replace(&mut self.entries[victim_index as usize], node.clone());
         tracing::debug!("evicting red node {:?} from {}", victim_node, victim_index);
         victim_node.lru_index().clear();
         self.promote_red_to_green(node, victim_index);
@@ -231,7 +232,7 @@ where
     ///
     /// NB: It is not required that `node.lru_index()` is up-to-date
     /// when entering this method.
-    fn promote_red_to_green(&mut self, node: &Arc, red_index: usize) {
+    fn promote_red_to_green(&mut self, node: &Arc, red_index: u16) {
         debug_assert!(self.red_zone().contains(&red_index));
 
         // Pick a yellow at random and switch places with it.
@@ -242,12 +243,12 @@ where
         let yellow_index = self.pick_index(self.yellow_zone());
         tracing::debug!(
             "demoting yellow node {:?} from {} to red at {}",
-            self.entries[yellow_index],
+            self.entries[yellow_index as usize],
             yellow_index,
             red_index,
         );
-        self.entries.swap(yellow_index, red_index);
-        self.entries[red_index].lru_index().store(red_index);
+        self.entries.swap(yellow_index as usize, red_index as usize);
+        self.entries[red_index as usize].lru_index().store(red_index);
 
         // Now move ourselves up into the green zone.
         self.promote_yellow_to_green(node, yellow_index);
@@ -259,51 +260,51 @@ where
     ///
     /// NB: It is not required that `node.lru_index()` is up-to-date
     /// when entering this method.
-    fn promote_yellow_to_green(&mut self, node: &Arc, yellow_index: usize) {
+    fn promote_yellow_to_green(&mut self, node: &Arc, yellow_index: u16) {
         debug_assert!(self.yellow_zone().contains(&yellow_index));
 
         // Pick a yellow at random and switch places with it.
         let green_index = self.pick_index(self.green_zone());
         tracing::debug!(
             "demoting green node {:?} from {} to yellow at {}",
-            self.entries[green_index],
+            self.entries[green_index as usize],
             green_index,
             yellow_index
         );
-        self.entries.swap(green_index, yellow_index);
-        self.entries[yellow_index].lru_index().store(yellow_index);
+        self.entries.swap(green_index as usize, yellow_index as usize);
+        self.entries[yellow_index as usize].lru_index().store(yellow_index);
         node.lru_index().store(green_index);
 
         tracing::debug!("promoted {:?} to green index {}", node, green_index);
     }
 
-    fn pick_index(&mut self, zone: std::ops::Range) -> usize {
-        let end_index = std::cmp::min(zone.end, self.entries.len());
-        self.rng.rand_range(zone.start as u64..end_index as u64) as usize
+    fn pick_index(&mut self, zone: std::ops::Range) -> u16 {
+        let end_index = std::cmp::min(zone.end, self.entries.len() as u16);
+        self.rng.rand_range(zone.start as u64..end_index as u64) as u16
     }
 }
 
 impl Default for LruIndex {
     fn default() -> Self {
-        Self { index: AtomicUsize::new(usize::MAX) }
+        Self { index: AtomicU16::new(u16::MAX) }
     }
 }
 
 impl LruIndex {
-    fn load(&self) -> usize {
+    fn load(&self) -> u16 {
         self.index.load(Ordering::Acquire) // see note on ordering below
     }
 
-    fn store(&self, value: usize) {
+    fn store(&self, value: u16) {
         self.index.store(value, Ordering::Release) // see note on ordering below
     }
 
     fn clear(&self) {
-        self.store(usize::MAX);
+        self.store(u16::MAX);
     }
 
     fn is_in_lru(&self) -> bool {
-        self.load() != usize::MAX
+        self.load() != u16::MAX
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs b/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
index 1dfde639869ab..e96b9daa979fc 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs
@@ -12,8 +12,9 @@ use std::fmt::Debug;
 use std::hash::Hash;
 use triomphe::Arc;
 
-pub use crate::derived::DependencyStorage;
 pub use crate::derived::MemoizedStorage;
+pub use crate::derived_lru::DependencyStorage as LruDependencyStorage;
+pub use crate::derived_lru::MemoizedStorage as LruMemoizedStorage;
 pub use crate::input::{InputStorage, UnitInputStorage};
 pub use crate::interned::InternedStorage;
 pub use crate::interned::LookupInternedStorage;
@@ -228,7 +229,7 @@ where
 /// that is, storage whose value is not derived from other storage but
 /// is set independently.
 pub trait LruQueryStorageOps {
-    fn set_lru_capacity(&self, new_capacity: usize);
+    fn set_lru_capacity(&self, new_capacity: u16);
 }
 
 pub trait DerivedQueryStorageOps
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
index 4f3341f51509a..5fe5f4b46d3e8 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/runtime.rs
@@ -18,7 +18,7 @@ use dependency_graph::DependencyGraph;
 pub(crate) mod local_state;
 use local_state::LocalState;
 
-use self::local_state::{ActiveQueryGuard, QueryInputs, QueryRevisions};
+use self::local_state::{ActiveQueryGuard, QueryRevisions};
 
 /// The salsa runtime stores the storage for all queries as well as
 /// tracking the query stack and dependencies between cycles.
@@ -558,21 +558,25 @@ impl ActiveQuery {
     }
 
     pub(crate) fn revisions(&self) -> QueryRevisions {
-        let inputs = match &self.dependencies {
-            None => QueryInputs::Untracked,
+        let (inputs, untracked) = match &self.dependencies {
+            None => (None, true),
 
-            Some(dependencies) => {
+            Some(dependencies) => (
                 if dependencies.is_empty() {
-                    QueryInputs::NoInputs
+                    None
                 } else {
-                    QueryInputs::Tracked {
-                        inputs: ThinArc::from_header_and_iter((), dependencies.iter().copied()),
-                    }
-                }
-            }
+                    Some(ThinArc::from_header_and_iter((), dependencies.iter().copied()))
+                },
+                false,
+            ),
         };
 
-        QueryRevisions { changed_at: self.changed_at, inputs, durability: self.durability }
+        QueryRevisions {
+            changed_at: self.changed_at,
+            inputs,
+            untracked,
+            durability: self.durability,
+        }
     }
 
     /// Adds any dependencies from `other` into `self`.
diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
index 0dbea1d563ef7..738696718868f 100644
--- a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
+++ b/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs
@@ -34,21 +34,13 @@ pub(crate) struct QueryRevisions {
     /// Minimum durability of the inputs to this query.
     pub(crate) durability: Durability,
 
-    /// The inputs that went into our query, if we are tracking them.
-    pub(crate) inputs: QueryInputs,
-}
-
-/// Every input.
-#[derive(Debug, Clone)]
-pub(crate) enum QueryInputs {
-    /// Non-empty set of inputs, fully known
-    Tracked { inputs: ThinArc<(), DatabaseKeyIndex> },
-
-    /// Empty set of inputs, fully known.
-    NoInputs,
+    /// Whether the input is untracked.
+    /// Invariant: if `untracked`, `inputs` is `None`.
+    /// Why is this encoded like this and not a proper enum? Struct size, this saves us 8 bytes.
+    pub(crate) untracked: bool,
 
-    /// Unknown quantity of inputs
-    Untracked,
+    /// The inputs that went into our query, if we are tracking them.
+    pub(crate) inputs: Option>,
 }
 
 impl Default for LocalState {
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/lru.rs b/src/tools/rust-analyzer/crates/salsa/tests/lru.rs
index 3da8519b08187..ef98a2c32b49e 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/lru.rs
+++ b/src/tools/rust-analyzer/crates/salsa/tests/lru.rs
@@ -24,7 +24,9 @@ impl Drop for HotPotato {
 
 #[salsa::query_group(QueryGroupStorage)]
 trait QueryGroup: salsa::Database {
+    #[salsa::lru]
     fn get(&self, x: u32) -> Arc;
+    #[salsa::lru]
     fn get_volatile(&self, x: u32) -> usize;
 }
 
diff --git a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs b/src/tools/rust-analyzer/crates/salsa/tests/macros.rs
index 3d818e53c8e93..9b07740e7de3d 100644
--- a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/salsa/tests/macros.rs
@@ -5,6 +5,7 @@ trait MyDatabase: salsa::Database {
 }
 
 mod another_module {
+    #[allow(dead_code)]
     pub(crate) fn another_name(_: &dyn crate::MyDatabase, (): ()) {}
 }
 
diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
index 332745aae6e95..b61baa224465f 100644
--- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs
+++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
@@ -80,13 +80,11 @@ macro_rules! register_ast_id_node {
 }
 register_ast_id_node! {
     impl AstIdNode for
-    Item,
+    Item, AnyHasGenericParams,
         Adt,
             Enum,
                 Variant,
             Struct,
-                RecordField,
-                TupleField,
             Union,
         AssocItem,
             Const,
@@ -104,7 +102,7 @@ register_ast_id_node! {
         Trait,
         TraitAlias,
         Use,
-    BlockExpr, ConstArg, Param, SelfParam
+    BlockExpr, ConstArg
 }
 
 /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs
index bbaf1b2a6d59d..b4e21d64f81e3 100644
--- a/src/tools/rust-analyzer/crates/span/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/span/src/lib.rs
@@ -17,18 +17,6 @@ pub use syntax::Edition;
 pub use text_size::{TextRange, TextSize};
 pub use vfs::FileId;
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct FilePosition {
-    pub file_id: FileId,
-    pub offset: TextSize,
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
-pub struct FileRange {
-    pub file_id: FileId,
-    pub range: TextRange,
-}
-
 // The first index is always the root node's AstId
 /// The root ast id always points to the encompassing file, using this in spans is discouraged as
 /// any range relative to it will be effectively absolute, ruining the entire point of anchored
@@ -45,6 +33,16 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
 
 pub type Span = SpanData;
 
+impl Span {
+    pub fn cover(self, other: Span) -> Span {
+        if self.anchor != other.anchor {
+            return self;
+        }
+        let range = self.range.cover(other.range);
+        Span { range, ..self }
+    }
+}
+
 /// 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.
@@ -63,7 +61,7 @@ pub struct SpanData {
 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)?;
+            fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
             f.write_char(':')?;
             fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
             f.write_char('@')?;
@@ -88,7 +86,7 @@ impl SpanData {
 
 impl fmt::Display for Span {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Debug::fmt(&self.anchor.file_id.index(), f)?;
+        fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
         f.write_char(':')?;
         fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
         f.write_char('@')?;
@@ -100,7 +98,7 @@ impl fmt::Display for Span {
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 pub struct SpanAnchor {
-    pub file_id: FileId,
+    pub file_id: EditionedFileId,
     pub ast_id: ErasedFileAstId,
 }
 
@@ -110,6 +108,81 @@ impl fmt::Debug for SpanAnchor {
     }
 }
 
+/// A [`FileId`] and [`Edition`] bundled up together.
+/// The MSB is reserved for `HirFileId` encoding, more upper bits are used to then encode the edition.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct EditionedFileId(u32);
+
+impl fmt::Debug for EditionedFileId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_tuple("EditionedFileId").field(&self.file_id()).field(&self.edition()).finish()
+    }
+}
+
+impl From for FileId {
+    fn from(value: EditionedFileId) -> Self {
+        value.file_id()
+    }
+}
+
+const _: () = assert!(
+    EditionedFileId::RESERVED_HIGH_BITS
+        + EditionedFileId::EDITION_BITS
+        + EditionedFileId::FILE_ID_BITS
+        == u32::BITS
+);
+const _: () = assert!(
+    EditionedFileId::RESERVED_MASK ^ EditionedFileId::EDITION_MASK ^ EditionedFileId::FILE_ID_MASK
+        == 0xFFFF_FFFF
+);
+
+impl EditionedFileId {
+    pub const RESERVED_MASK: u32 = 0x8000_0000;
+    pub const EDITION_MASK: u32 = 0x7F80_0000;
+    pub const FILE_ID_MASK: u32 = 0x007F_FFFF;
+
+    pub const MAX_FILE_ID: u32 = Self::FILE_ID_MASK;
+
+    pub const RESERVED_HIGH_BITS: u32 = Self::RESERVED_MASK.count_ones();
+    pub const FILE_ID_BITS: u32 = Self::FILE_ID_MASK.count_ones();
+    pub const EDITION_BITS: u32 = Self::EDITION_MASK.count_ones();
+
+    pub const fn current_edition(file_id: FileId) -> Self {
+        Self::new(file_id, Edition::CURRENT)
+    }
+
+    pub const fn new(file_id: FileId, edition: Edition) -> Self {
+        let file_id = file_id.index();
+        let edition = edition as u32;
+        assert!(file_id <= Self::MAX_FILE_ID);
+        Self(file_id | (edition << Self::FILE_ID_BITS))
+    }
+
+    pub fn from_raw(u32: u32) -> Self {
+        assert!(u32 & Self::RESERVED_MASK == 0);
+        assert!((u32 & Self::EDITION_MASK) >> Self::FILE_ID_BITS <= Edition::LATEST as u32);
+        Self(u32)
+    }
+
+    pub const fn as_u32(self) -> u32 {
+        self.0
+    }
+
+    pub const fn file_id(self) -> FileId {
+        FileId::from_raw(self.0 & Self::FILE_ID_MASK)
+    }
+
+    pub const fn unpack(self) -> (FileId, Edition) {
+        (self.file_id(), self.edition())
+    }
+
+    pub const fn edition(self) -> Edition {
+        let edition = (self.0 & Self::EDITION_MASK) >> Self::FILE_ID_BITS;
+        debug_assert!(edition <= Edition::LATEST as u32);
+        unsafe { std::mem::transmute(edition as u8) }
+    }
+}
+
 /// Input to the analyzer is a set of files, where each file is identified by
 /// `FileId` and contains source code. However, another source of source code in
 /// Rust are macros: each macro can be thought of as producing a "temporary
@@ -149,6 +222,38 @@ impl fmt::Debug for HirFileId {
     }
 }
 
+impl PartialEq for HirFileId {
+    fn eq(&self, &other: &FileId) -> bool {
+        self.file_id().map(EditionedFileId::file_id) == Some(other)
+    }
+}
+impl PartialEq for FileId {
+    fn eq(&self, other: &HirFileId) -> bool {
+        other.file_id().map(EditionedFileId::file_id) == Some(*self)
+    }
+}
+
+impl PartialEq for HirFileId {
+    fn eq(&self, &other: &EditionedFileId) -> bool {
+        *self == HirFileId::from(other)
+    }
+}
+impl PartialEq for EditionedFileId {
+    fn eq(&self, &other: &HirFileId) -> bool {
+        other == HirFileId::from(*self)
+    }
+}
+impl PartialEq for FileId {
+    fn eq(&self, &other: &EditionedFileId) -> bool {
+        *self == FileId::from(other)
+    }
+}
+impl PartialEq for EditionedFileId {
+    fn eq(&self, &other: &FileId) -> bool {
+        other == FileId::from(*self)
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct MacroFileId {
     pub macro_call_id: MacroCallId,
@@ -182,14 +287,14 @@ impl MacroCallId {
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub enum HirFileIdRepr {
-    FileId(FileId),
+    FileId(EditionedFileId),
     MacroFile(MacroFileId),
 }
 
 impl fmt::Debug for HirFileIdRepr {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.index()).finish(),
+            Self::FileId(arg0) => arg0.fmt(f),
             Self::MacroFile(arg0) => {
                 f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish()
             }
@@ -197,19 +302,17 @@ impl fmt::Debug for HirFileIdRepr {
     }
 }
 
-impl From for HirFileId {
+impl From for HirFileId {
     #[allow(clippy::let_unit_value)]
-    fn from(id: FileId) -> Self {
-        _ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
-        assert!(id.index() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.index());
-        HirFileId(id.index())
+    fn from(id: EditionedFileId) -> Self {
+        assert!(id.as_u32() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.as_u32());
+        HirFileId(id.as_u32())
     }
 }
 
 impl From for HirFileId {
     #[allow(clippy::let_unit_value)]
     fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self {
-        _ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
         let id = id.as_u32();
         assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {id} is too large");
         HirFileId(id | Self::MACRO_FILE_TAG_MASK)
@@ -217,9 +320,6 @@ impl From for HirFileId {
 }
 
 impl HirFileId {
-    const ASSERT_MAX_FILE_ID_IS_SAME: () =
-        [()][(Self::MAX_HIR_FILE_ID != FileId::MAX_FILE_ID) as usize];
-
     const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
     const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
 
@@ -239,9 +339,9 @@ impl HirFileId {
     }
 
     #[inline]
-    pub fn file_id(self) -> Option {
+    pub fn file_id(self) -> Option {
         match self.0 & Self::MACRO_FILE_TAG_MASK {
-            0 => Some(FileId::from_raw(self.0)),
+            0 => Some(EditionedFileId(self.0)),
             _ => None,
         }
     }
@@ -249,7 +349,7 @@ impl HirFileId {
     #[inline]
     pub fn repr(self) -> HirFileIdRepr {
         match self.0 & Self::MACRO_FILE_TAG_MASK {
-            0 => HirFileIdRepr::FileId(FileId::from_raw(self.0)),
+            0 => HirFileIdRepr::FileId(EditionedFileId(self.0)),
             _ => HirFileIdRepr::MacroFile(MacroFileId {
                 macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
             }),
diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs
index 81fc56c961e46..6269f4c30c7d2 100644
--- a/src/tools/rust-analyzer/crates/span/src/map.rs
+++ b/src/tools/rust-analyzer/crates/span/src/map.rs
@@ -4,11 +4,10 @@
 use std::{fmt, hash::Hash};
 
 use stdx::{always, itertools::Itertools};
-use vfs::FileId;
 
 use crate::{
-    ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, TextRange, TextSize,
-    ROOT_ERASED_FILE_AST_ID,
+    EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, TextRange,
+    TextSize, ROOT_ERASED_FILE_AST_ID,
 };
 
 /// Maps absolute text ranges for the corresponding file to the relevant span data.
@@ -109,7 +108,7 @@ where
 
 #[derive(PartialEq, Eq, Hash, Debug)]
 pub struct RealSpanMap {
-    file_id: FileId,
+    file_id: EditionedFileId,
     /// Invariant: Sorted vec over TextSize
     // FIXME: SortedVec<(TextSize, ErasedFileAstId)>?
     pairs: Box<[(TextSize, ErasedFileAstId)]>,
@@ -128,7 +127,7 @@ impl fmt::Display for RealSpanMap {
 
 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 {
+    pub fn absolute(file_id: EditionedFileId) -> Self {
         RealSpanMap {
             file_id,
             pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]),
@@ -137,7 +136,7 @@ impl RealSpanMap {
     }
 
     pub fn from_file(
-        file_id: FileId,
+        file_id: EditionedFileId,
         pairs: Box<[(TextSize, ErasedFileAstId)]>,
         end: TextSize,
     ) -> Self {
diff --git a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs
index 4eafcfb060f22..91fab8e92380f 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs
@@ -271,12 +271,12 @@ macro_rules! implement {
 
             #[inline]
             unsafe fn downcast_ref_unchecked(&self) -> &T {
-                &*(self as *const Self as *const T)
+                unsafe { &*(self as *const Self as *const T) }
             }
 
             #[inline]
             unsafe fn downcast_mut_unchecked(&mut self) -> &mut T {
-                &mut *(self as *mut Self as *mut T)
+                unsafe { &mut *(self as *mut Self as *mut T) }
             }
         }
 
diff --git a/src/tools/rust-analyzer/crates/stdx/src/process.rs b/src/tools/rust-analyzer/crates/stdx/src/process.rs
index c54d850d7b56e..75ae064db9a4d 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/process.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/process.rs
@@ -212,17 +212,13 @@ mod imp {
 
     impl<'a> Pipe<'a> {
         unsafe fn new(p: P, dst: &'a mut Vec) -> Pipe<'a> {
-            Pipe {
-                dst,
-                pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
-                overlapped: Overlapped::zero(),
-                done: false,
-            }
+            let pipe = unsafe { NamedPipe::from_raw_handle(p.into_raw_handle()) };
+            Pipe { dst, pipe, overlapped: Overlapped::zero(), done: false }
         }
 
         unsafe fn read(&mut self) -> io::Result<()> {
-            let dst = slice_to_end(self.dst);
-            match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
+            let dst = unsafe { slice_to_end(self.dst) };
+            match unsafe { self.pipe.read_overlapped(dst, self.overlapped.raw()) } {
                 Ok(_) => Ok(()),
                 Err(e) => {
                     if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
@@ -237,7 +233,7 @@ mod imp {
 
         unsafe fn complete(&mut self, status: &CompletionStatus) {
             let prev = self.dst.len();
-            self.dst.set_len(prev + status.bytes_transferred() as usize);
+            unsafe { self.dst.set_len(prev + status.bytes_transferred() as usize) };
             if status.bytes_transferred() == 0 {
                 self.done = true;
             }
@@ -251,7 +247,9 @@ mod imp {
         if v.capacity() == v.len() {
             v.reserve(1);
         }
-        slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len())
+        let data = unsafe { v.as_mut_ptr().add(v.len()) };
+        let len = v.capacity() - v.len();
+        unsafe { slice::from_raw_parts_mut(data, len) }
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 8c772b9c7a2aa..c23bcd6914910 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -8,7 +8,11 @@
 //
 //   //          -- comment
 //   Name =      -- non-terminal definition
-//   'ident'     -- token (terminal)
+//   'ident'     -- keyword or punct token (terminal)
+//   '?ident'    -- contextual keyword (terminal)
+//   too)
+//   '#ident'    -- generic token (terminal)
+//   '@ident'    -- literal token (terminal)
 //   A B         -- sequence
 //   A | B       -- alternation
 //   A*          -- zero or more repetition
@@ -17,17 +21,17 @@
 //   label:A     -- suggested name for field of AST node
 
 //*************************//
-// Names, Paths and Macros //
+//         Paths           //
 //*************************//
 
 Name =
-  'ident' | 'self'
+  '#ident' | 'self'
 
 NameRef =
-  'ident' | 'int_number' | 'self' | 'super' | 'crate' | 'Self'
+  '#ident' | '@int_number' | 'self' | 'super' | 'crate' | 'Self'
 
 Lifetime =
-  'lifetime_ident'
+  '#lifetime_ident'
 
 Path =
   (qualifier:Path '::')? segment:PathSegment
@@ -38,6 +42,11 @@ PathSegment =
 | NameRef ParamList RetType?
 | '<' Type ('as' PathType)? '>'
 
+
+//*************************//
+//        Generics         //
+//*************************//
+
 GenericArgList =
   '::'? '<' (GenericArg (',' GenericArg)* ','?)? '>'
 
@@ -61,6 +70,36 @@ LifetimeArg =
 ConstArg =
   Expr
 
+GenericParamList =
+  '<' (GenericParam (',' GenericParam)* ','?)? '>'
+
+GenericParam =
+  ConstParam
+| LifetimeParam
+| TypeParam
+
+TypeParam =
+  Attr* Name (':' TypeBoundList?)?
+  ('=' default_type:Type)?
+
+ConstParam =
+  Attr* 'const' Name ':' Type
+  ('=' default_val:ConstArg)?
+
+LifetimeParam =
+  Attr* Lifetime (':' TypeBoundList?)?
+
+WhereClause =
+  'where' predicates:(WherePred (',' WherePred)* ','?)
+
+WherePred =
+  ('for' GenericParamList)?  (Lifetime | Type) ':' TypeBoundList?
+
+
+//*************************//
+//          Macro          //
+//*************************//
+
 MacroCall =
   Attr* Path '!' TokenTree ';'?
 
@@ -72,22 +111,23 @@ TokenTree =
 MacroItems =
   Item*
 
-MacroEagerInput =
-  '(' (Expr (',' Expr)* ','?)? ')'
-| '{' (Expr (',' Expr)* ','?)? '}'
-| '[' (Expr (',' Expr)* ','?)? ']'
-
-
 MacroStmts =
   statements:Stmt*
   Expr?
 
+Attr =
+  '#' '!'? '[' Meta ']'
+
+Meta =
+  'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
+| Path ('=' Expr | TokenTree)?
+
 //*************************//
 //          Items          //
 //*************************//
 
 SourceFile =
-  'shebang'?
+  '#shebang'?
   Attr*
   Item*
 
@@ -112,7 +152,7 @@ Item =
 
 MacroRules =
   Attr* Visibility?
-  'macro_rules' '!' Name
+  '?macro_rules' '!' Name
   TokenTree
 
 MacroDef =
@@ -148,12 +188,12 @@ UseTreeList =
 
 Fn =
  Attr* Visibility?
- 'default'? 'const'? 'async'? 'unsafe'? Abi?
+ '?default'? 'const'? 'async'? 'unsafe'? Abi?
  'fn' Name GenericParamList? ParamList RetType? WhereClause?
  (body:BlockExpr | ';')
 
 Abi =
-  'extern' 'string'?
+  'extern' '@string'?
 
 ParamList =
   '('(
@@ -180,7 +220,7 @@ RetType =
 
 TypeAlias =
   Attr* Visibility?
-  'default'?
+  '?default'?
   'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause?
   ('=' Type)? ';'
 
@@ -223,7 +263,7 @@ Variant =
 
 Union =
   Attr* Visibility?
-  'union' Name GenericParamList? WhereClause?
+  '?union' Name GenericParamList? WhereClause?
   RecordFieldList
 
 // A Data Type.
@@ -236,7 +276,7 @@ Adt =
 
 Const =
   Attr* Visibility?
-  'default'?
+  '?default'?
   'const' (Name | '_') ':' Type
   ('=' body:Expr)? ';'
 
@@ -247,7 +287,7 @@ Static =
 
 Trait =
   Attr* Visibility?
-  'unsafe'? 'auto'?
+  'unsafe'? '?auto'?
   'trait' Name GenericParamList?
   (':' TypeBoundList?)? WhereClause? AssocItemList
 
@@ -266,7 +306,7 @@ AssocItem =
 
 Impl =
   Attr* Visibility?
-  'default'? 'unsafe'?
+  '?default'? 'unsafe'?
   'impl' GenericParamList? ('const'? '!'? trait:Type 'for')? self_ty:Type WhereClause?
   AssocItemList
 
@@ -282,41 +322,9 @@ ExternItem =
 | Static
 | TypeAlias
 
-GenericParamList =
-  '<' (GenericParam (',' GenericParam)* ','?)? '>'
-
-GenericParam =
-  ConstParam
-| LifetimeParam
-| TypeParam
-
-TypeParam =
-  Attr* Name (':' TypeBoundList?)?
-  ('=' default_type:Type)?
-
-ConstParam =
-  Attr* 'const' Name ':' Type
-  ('=' default_val:ConstArg)?
-
-LifetimeParam =
-  Attr* Lifetime (':' TypeBoundList?)?
-
-WhereClause =
-  'where' predicates:(WherePred (',' WherePred)* ','?)
-
-WherePred =
-  ('for' GenericParamList)?  (Lifetime | Type) ':' TypeBoundList?
-
 Visibility =
   'pub' ('(' 'in'? Path ')')?
 
-Attr =
-  '#' '!'? '[' Meta ']'
-
-Meta =
-  'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
-| Path ('=' Expr | TokenTree)?
-
 
 //****************************//
 // Statements and Expressions //
@@ -379,13 +387,13 @@ Expr =
 | UnderscoreExpr
 
 OffsetOfExpr =
-  Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
+  Attr* '?builtin' '#' '?offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
 
 AsmExpr =
-  Attr* 'builtin' '#' 'asm' '(' Expr ')'
+  Attr* '?builtin' '#' '?asm' '(' Expr ')'
 
 FormatArgsExpr =
-  Attr* 'builtin' '#' 'format_args' '('
+  Attr* '?builtin' '#' '?format_args' '('
   template:Expr
   (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
   ')'
@@ -398,11 +406,12 @@ MacroExpr =
 
 Literal =
   Attr* value:(
-    'int_number' | 'float_number'
-  | 'string' | 'raw_string'
-  | 'byte_string' | 'raw_byte_string'
+    '@int_number' | '@float_number'
+  | '@string' | '@raw_string'
+  | '@byte_string' | '@raw_byte_string'
+  | '@c_string' | '@raw_c_string'
+  | '@char' | '@byte'
   | 'true' | 'false'
-  | 'char' | 'byte'
   )
 
 PathExpr =
@@ -416,13 +425,13 @@ StmtList =
   '}'
 
 RefExpr =
-  Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr
+  Attr* '&' (('?raw' 'const'?)| ('?raw'? 'mut') ) Expr
 
 TryExpr =
   Attr* Expr '?'
 
 BlockExpr =
-  Attr* Label? ('try' | 'unsafe' | 'async' | 'const') StmtList
+  Attr* Label? ('try' | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList
 
 PrefixExpr =
   Attr* op:('-' | '!' | '*') Expr
@@ -482,9 +491,12 @@ FieldExpr =
   Attr* Expr '.' NameRef
 
 ClosureExpr =
-  Attr* ('for' GenericParamList)? 'const'? 'static'? 'async'? 'move'?  ParamList RetType?
+  Attr* ClosureBinder? 'const'? 'static'? 'async'? 'gen'? 'move'?  ParamList RetType?
   body:Expr
 
+ClosureBinder =
+  'for' GenericParamList
+
 IfExpr =
   Attr* 'if' condition:Expr then_branch:BlockExpr
   ('else' else_branch:(IfExpr | BlockExpr))?
@@ -538,7 +550,7 @@ YieldExpr =
   Attr* 'yield' Expr?
 
 YeetExpr =
-  Attr* 'do' 'yeet' Expr?
+  Attr* 'do' '?yeet' Expr?
 
 LetExpr =
   Attr* 'let' Pat '=' Expr
@@ -617,6 +629,7 @@ TypeBoundList =
 TypeBound =
   Lifetime
 | ('~' 'const' | 'const')? 'async'? '?'? Type
+| 'use' GenericParamList
 
 //************************//
 //        Patterns        //
diff --git a/src/tools/rust-analyzer/crates/syntax/src/algo.rs b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
index 9b43da83418f0..8dc6d36a7e799 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/algo.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
@@ -643,7 +643,7 @@ fn main() {
         let deletions = diff
             .deletions
             .iter()
-            .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), &fmt_syntax(v))));
+            .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), fmt_syntax(v))));
 
         let actual = format!(
             "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n"
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
index b0ee9dfd5071d..6ed205e2856f7 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
@@ -352,13 +352,22 @@ pub enum BlockModifier {
     Unsafe(SyntaxToken),
     Try(SyntaxToken),
     Const(SyntaxToken),
+    AsyncGen(SyntaxToken),
+    Gen(SyntaxToken),
     Label(ast::Label),
 }
 
 impl ast::BlockExpr {
     pub fn modifier(&self) -> Option {
-        self.async_token()
-            .map(BlockModifier::Async)
+        self.gen_token()
+            .map(|v| {
+                if self.async_token().is_some() {
+                    BlockModifier::AsyncGen(v)
+                } else {
+                    BlockModifier::Gen(v)
+                }
+            })
+            .or_else(|| self.async_token().map(BlockModifier::Async))
             .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
             .or_else(|| self.try_token().map(BlockModifier::Try))
             .or_else(|| self.const_token().map(BlockModifier::Const))
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 0373e7c5529fc..01886d119d675 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -14,6 +14,8 @@ pub struct Abi {
 impl Abi {
     #[inline]
     pub fn extern_token(&self) -> Option { support::token(&self.syntax, T![extern]) }
+    #[inline]
+    pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -182,6 +184,10 @@ impl BlockExpr {
     #[inline]
     pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) }
     #[inline]
+    pub fn gen_token(&self) -> Option { support::token(&self.syntax, T![gen]) }
+    #[inline]
+    pub fn move_token(&self) -> Option { support::token(&self.syntax, T![move]) }
+    #[inline]
     pub fn try_token(&self) -> Option { support::token(&self.syntax, T![try]) }
     #[inline]
     pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) }
@@ -237,6 +243,17 @@ impl CastExpr {
     pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ClosureBinder {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ClosureBinder {
+    #[inline]
+    pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) }
+    #[inline]
+    pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct ClosureExpr {
     pub(crate) syntax: SyntaxNode,
@@ -244,7 +261,7 @@ pub struct ClosureExpr {
 impl ast::HasAttrs for ClosureExpr {}
 impl ClosureExpr {
     #[inline]
-    pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) }
+    pub fn closure_binder(&self) -> Option { support::child(&self.syntax) }
     #[inline]
     pub fn param_list(&self) -> Option { support::child(&self.syntax) }
     #[inline]
@@ -254,7 +271,7 @@ impl ClosureExpr {
     #[inline]
     pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) }
     #[inline]
-    pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) }
+    pub fn gen_token(&self) -> Option { support::token(&self.syntax, T![gen]) }
     #[inline]
     pub fn move_token(&self) -> Option { support::token(&self.syntax, T![move]) }
     #[inline]
@@ -833,27 +850,6 @@ impl MacroDef {
     pub fn macro_token(&self) -> Option { support::token(&self.syntax, T![macro]) }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct MacroEagerInput {
-    pub(crate) syntax: SyntaxNode,
-}
-impl MacroEagerInput {
-    #[inline]
-    pub fn exprs(&self) -> AstChildren { support::children(&self.syntax) }
-    #[inline]
-    pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) }
-    #[inline]
-    pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) }
-    #[inline]
-    pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) }
-    #[inline]
-    pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) }
-    #[inline]
-    pub fn l_curly_token(&self) -> Option { support::token(&self.syntax, T!['{']) }
-    #[inline]
-    pub fn r_curly_token(&self) -> Option { support::token(&self.syntax, T!['}']) }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct MacroExpr {
     pub(crate) syntax: SyntaxNode,
@@ -1050,6 +1046,10 @@ impl NameRef {
     #[inline]
     pub fn ident_token(&self) -> Option { support::token(&self.syntax, T![ident]) }
     #[inline]
+    pub fn int_number_token(&self) -> Option {
+        support::token(&self.syntax, T![int_number])
+    }
+    #[inline]
     pub fn self_token(&self) -> Option { support::token(&self.syntax, T![self]) }
     #[inline]
     pub fn super_token(&self) -> Option { support::token(&self.syntax, T![super]) }
@@ -1788,6 +1788,8 @@ pub struct TypeBound {
     pub(crate) syntax: SyntaxNode,
 }
 impl TypeBound {
+    #[inline]
+    pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) }
     #[inline]
     pub fn lifetime(&self) -> Option { support::child(&self.syntax) }
     #[inline]
@@ -1799,6 +1801,8 @@ impl TypeBound {
     #[inline]
     pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) }
     #[inline]
+    pub fn use_token(&self) -> Option { support::token(&self.syntax, T![use]) }
+    #[inline]
     pub fn tilde_token(&self) -> Option { support::token(&self.syntax, T![~]) }
 }
 
@@ -2461,6 +2465,20 @@ impl AstNode for CastExpr {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for ClosureBinder {
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_BINDER }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for ClosureExpr {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_EXPR }
@@ -3021,20 +3039,6 @@ impl AstNode for MacroDef {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
-impl AstNode for MacroEagerInput {
-    #[inline]
-    fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EAGER_INPUT }
-    #[inline]
-    fn cast(syntax: SyntaxNode) -> Option {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
-    }
-    #[inline]
-    fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
 impl AstNode for MacroExpr {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EXPR }
@@ -5106,6 +5110,14 @@ impl AstNode for AnyHasArgList {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasArgList {
+    #[inline]
+    fn from(node: CallExpr) -> AnyHasArgList { AnyHasArgList { syntax: node.syntax } }
+}
+impl From for AnyHasArgList {
+    #[inline]
+    fn from(node: MethodCallExpr) -> AnyHasArgList { AnyHasArgList { syntax: node.syntax } }
+}
 impl AnyHasAttrs {
     #[inline]
     pub fn new(node: T) -> AnyHasAttrs {
@@ -5198,6 +5210,294 @@ impl AstNode for AnyHasAttrs {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ArrayExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: AsmExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: AssocItemList) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: AwaitExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: BecomeExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: BinExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: BlockExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: BreakExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: CallExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: CastExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ClosureExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Const) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ConstParam) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ContinueExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Enum) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ExternBlock) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ExternCrate) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ExternItemList) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: FieldExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Fn) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ForExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: FormatArgsExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: IdentPat) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: IfExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Impl) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: IndexExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ItemList) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: LetExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: LetStmt) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: LifetimeParam) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Literal) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: LoopExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MacroCall) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MacroDef) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MacroRules) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MatchArm) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MatchArmList) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MatchExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: MethodCallExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Module) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: OffsetOfExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Param) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ParenExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: PathExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: PrefixExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RangeExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RecordExprField) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RecordExprFieldList) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RecordField) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RecordPatField) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RefExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: RestPat) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: ReturnExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: SelfParam) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: SourceFile) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Static) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: StmtList) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Struct) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Trait) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: TraitAlias) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: TryExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: TupleExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: TupleField) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: TypeAlias) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: TypeParam) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: UnderscoreExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Union) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Use) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: Variant) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: WhileExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: YeetExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
+impl From for AnyHasAttrs {
+    #[inline]
+    fn from(node: YieldExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } }
+}
 impl AnyHasDocComments {
     #[inline]
     pub fn new(node: T) -> AnyHasDocComments {
@@ -5239,6 +5539,90 @@ impl AstNode for AnyHasDocComments {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Const) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Enum) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: ExternBlock) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: ExternCrate) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Fn) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Impl) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: MacroCall) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: MacroDef) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: MacroRules) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Module) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: RecordField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: SourceFile) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Static) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Struct) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Trait) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: TraitAlias) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: TupleField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: TypeAlias) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Union) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Use) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
+impl From for AnyHasDocComments {
+    #[inline]
+    fn from(node: Variant) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } }
+}
 impl AnyHasGenericArgs {
     #[inline]
     pub fn new(node: T) -> AnyHasGenericArgs {
@@ -5257,6 +5641,18 @@ impl AstNode for AnyHasGenericArgs {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasGenericArgs {
+    #[inline]
+    fn from(node: AssocTypeArg) -> AnyHasGenericArgs { AnyHasGenericArgs { syntax: node.syntax } }
+}
+impl From for AnyHasGenericArgs {
+    #[inline]
+    fn from(node: MethodCallExpr) -> AnyHasGenericArgs { AnyHasGenericArgs { syntax: node.syntax } }
+}
+impl From for AnyHasGenericArgs {
+    #[inline]
+    fn from(node: PathSegment) -> AnyHasGenericArgs { AnyHasGenericArgs { syntax: node.syntax } }
+}
 impl AnyHasGenericParams {
     #[inline]
     pub fn new(node: T) -> AnyHasGenericParams {
@@ -5275,6 +5671,38 @@ impl AstNode for AnyHasGenericParams {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: Enum) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: Fn) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: Impl) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: Struct) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: Trait) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: TraitAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: TypeAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
+impl From for AnyHasGenericParams {
+    #[inline]
+    fn from(node: Union) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } }
+}
 impl AnyHasLoopBody {
     #[inline]
     pub fn new(node: T) -> AnyHasLoopBody {
@@ -5291,6 +5719,18 @@ impl AstNode for AnyHasLoopBody {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasLoopBody {
+    #[inline]
+    fn from(node: ForExpr) -> AnyHasLoopBody { AnyHasLoopBody { syntax: node.syntax } }
+}
+impl From for AnyHasLoopBody {
+    #[inline]
+    fn from(node: LoopExpr) -> AnyHasLoopBody { AnyHasLoopBody { syntax: node.syntax } }
+}
+impl From for AnyHasLoopBody {
+    #[inline]
+    fn from(node: WhileExpr) -> AnyHasLoopBody { AnyHasLoopBody { syntax: node.syntax } }
+}
 impl AnyHasModuleItem {
     #[inline]
     pub fn new(node: T) -> AnyHasModuleItem {
@@ -5307,6 +5747,18 @@ impl AstNode for AnyHasModuleItem {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasModuleItem {
+    #[inline]
+    fn from(node: ItemList) -> AnyHasModuleItem { AnyHasModuleItem { syntax: node.syntax } }
+}
+impl From for AnyHasModuleItem {
+    #[inline]
+    fn from(node: MacroItems) -> AnyHasModuleItem { AnyHasModuleItem { syntax: node.syntax } }
+}
+impl From for AnyHasModuleItem {
+    #[inline]
+    fn from(node: SourceFile) -> AnyHasModuleItem { AnyHasModuleItem { syntax: node.syntax } }
+}
 impl AnyHasName {
     #[inline]
     pub fn new(node: T) -> AnyHasName {
@@ -5347,6 +5799,86 @@ impl AstNode for AnyHasName {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: ConstParam) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Enum) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Fn) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: FormatArgsArg) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: IdentPat) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: MacroDef) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: MacroRules) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Module) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: RecordField) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Rename) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: SelfParam) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Static) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Struct) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Trait) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: TraitAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: TypeAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: TypeParam) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Union) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
+impl From for AnyHasName {
+    #[inline]
+    fn from(node: Variant) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
 impl AnyHasTypeBounds {
     #[inline]
     pub fn new(node: T) -> AnyHasTypeBounds {
@@ -5368,6 +5900,30 @@ impl AstNode for AnyHasTypeBounds {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasTypeBounds {
+    #[inline]
+    fn from(node: AssocTypeArg) -> AnyHasTypeBounds { AnyHasTypeBounds { syntax: node.syntax } }
+}
+impl From for AnyHasTypeBounds {
+    #[inline]
+    fn from(node: LifetimeParam) -> AnyHasTypeBounds { AnyHasTypeBounds { syntax: node.syntax } }
+}
+impl From for AnyHasTypeBounds {
+    #[inline]
+    fn from(node: Trait) -> AnyHasTypeBounds { AnyHasTypeBounds { syntax: node.syntax } }
+}
+impl From for AnyHasTypeBounds {
+    #[inline]
+    fn from(node: TypeAlias) -> AnyHasTypeBounds { AnyHasTypeBounds { syntax: node.syntax } }
+}
+impl From for AnyHasTypeBounds {
+    #[inline]
+    fn from(node: TypeParam) -> AnyHasTypeBounds { AnyHasTypeBounds { syntax: node.syntax } }
+}
+impl From for AnyHasTypeBounds {
+    #[inline]
+    fn from(node: WherePred) -> AnyHasTypeBounds { AnyHasTypeBounds { syntax: node.syntax } }
+}
 impl AnyHasVisibility {
     #[inline]
     pub fn new(node: T) -> AnyHasVisibility {
@@ -5406,6 +5962,78 @@ impl AstNode for AnyHasVisibility {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Const) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Enum) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: ExternCrate) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Fn) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Impl) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: MacroDef) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: MacroRules) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Module) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: RecordField) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Static) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Struct) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Trait) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: TraitAlias) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: TupleField) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: TypeAlias) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Union) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Use) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
+impl From for AnyHasVisibility {
+    #[inline]
+    fn from(node: Variant) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } }
+}
 impl std::fmt::Display for Adt {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
@@ -5541,6 +6169,11 @@ impl std::fmt::Display for CastExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for ClosureBinder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for ClosureExpr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
@@ -5741,11 +6374,6 @@ impl std::fmt::Display for MacroDef {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
-impl std::fmt::Display for MacroEagerInput {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        std::fmt::Display::fmt(self.syntax(), f)
-    }
-}
 impl std::fmt::Display for MacroExpr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 35ec9b1013d18..0228d9dd7135c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -179,18 +179,18 @@ pub fn ty_alias(
     }
 
     if let Some(list) = type_param_bounds {
-        s.push_str(&format!(" : {}", &list));
+        s.push_str(&format!(" : {list}"));
     }
 
     if let Some(cl) = where_clause {
-        s.push_str(&format!(" {}", &cl.to_string()));
+        s.push_str(&format!(" {cl}"));
     }
 
     if let Some(exp) = assignment {
         if let Some(cl) = exp.1 {
-            s.push_str(&format!(" = {} {}", &exp.0.to_string(), &cl.to_string()));
+            s.push_str(&format!(" = {} {cl}", exp.0));
         } else {
-            s.push_str(&format!(" = {}", &exp.0.to_string()));
+            s.push_str(&format!(" = {}", exp.0));
         }
     }
 
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 911e3d823de6f..5447906206c7f 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
@@ -794,6 +794,8 @@ pub enum TypeBoundKind {
     PathType(ast::PathType),
     /// for<'a> ...
     ForType(ast::ForType),
+    /// use
+    Use(ast::GenericParamList),
     /// 'a
     Lifetime(ast::Lifetime),
 }
@@ -804,6 +806,8 @@ impl ast::TypeBound {
             TypeBoundKind::PathType(path_type)
         } else if let Some(for_type) = support::children(self.syntax()).next() {
             TypeBoundKind::ForType(for_type)
+        } else if let Some(generic_param_list) = self.generic_param_list() {
+            TypeBoundKind::Use(generic_param_list)
         } else if let Some(lifetime) = self.lifetime() {
             TypeBoundKind::Lifetime(lifetime)
         } else {
@@ -1127,21 +1131,3 @@ impl From for ast::AnyHasAttrs {
         Self::new(node)
     }
 }
-
-impl From for ast::AnyHasAttrs {
-    fn from(node: ast::Variant) -> Self {
-        Self::new(node)
-    }
-}
-
-impl From for ast::AnyHasAttrs {
-    fn from(node: ast::RecordField) -> Self {
-        Self::new(node)
-    }
-}
-
-impl From for ast::AnyHasAttrs {
-    fn from(node: ast::TupleField) -> Self {
-        Self::new(node)
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 177f48b986a9b..b68374848b90c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -65,7 +65,7 @@ pub use rowan::{
     TokenAtOffset, WalkEvent,
 };
 pub use rustc_lexer::unescape;
-pub use smol_str::{format_smolstr, SmolStr};
+pub use smol_str::{format_smolstr, SmolStr, ToSmolStr};
 
 /// `Parse` is the result of the parsing: a syntax tree and a collection of
 /// errors.
@@ -150,15 +150,17 @@ impl Parse {
     }
 
     pub fn reparse(&self, indel: &Indel, edition: Edition) -> Parse {
-        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel, edition))
+        self.incremental_reparse(indel, edition)
+            .unwrap_or_else(|| self.full_reparse(indel, edition))
     }
 
-    fn incremental_reparse(&self, indel: &Indel) -> Option> {
+    fn incremental_reparse(&self, indel: &Indel, edition: Edition) -> Option> {
         // FIXME: validation errors are not handled here
         parsing::incremental_reparse(
             self.tree().syntax(),
             indel,
             self.errors.as_deref().unwrap_or_default().iter().cloned(),
+            edition,
         )
         .map(|(green_node, errors, _reparsed_range)| Parse {
             green: green_node,
@@ -211,115 +213,6 @@ impl SourceFile {
     }
 }
 
-impl ast::TokenTree {
-    pub fn reparse_as_comma_separated_expr(
-        self,
-        edition: parser::Edition,
-    ) -> Parse {
-        let tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
-
-        let mut parser_input = parser::Input::default();
-        let mut was_joint = false;
-        for t in tokens {
-            let kind = t.kind();
-            if kind.is_trivia() {
-                was_joint = false
-            } else if kind == SyntaxKind::IDENT {
-                let token_text = t.text();
-                let contextual_kw =
-                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT);
-                parser_input.push_ident(contextual_kw);
-            } else {
-                if was_joint {
-                    parser_input.was_joint();
-                }
-                parser_input.push(kind);
-                // Tag the token as joint if it is float with a fractional part
-                // we use this jointness to inform the parser about what token split
-                // event to emit when we encounter a float literal in a field access
-                if kind == SyntaxKind::FLOAT_NUMBER {
-                    if !t.text().ends_with('.') {
-                        parser_input.was_joint();
-                    } else {
-                        was_joint = false;
-                    }
-                } else {
-                    was_joint = true;
-                }
-            }
-        }
-
-        let parser_output = parser::TopEntryPoint::MacroEagerInput.parse(&parser_input, edition);
-
-        let mut tokens =
-            self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token);
-        let mut text = String::new();
-        let mut pos = TextSize::from(0);
-        let mut builder = SyntaxTreeBuilder::default();
-        for event in parser_output.iter() {
-            match event {
-                parser::Step::Token { kind, n_input_tokens } => {
-                    let mut token = tokens.next().unwrap();
-                    while token.kind().is_trivia() {
-                        let text = token.text();
-                        pos += TextSize::from(text.len() as u32);
-                        builder.token(token.kind(), text);
-
-                        token = tokens.next().unwrap();
-                    }
-                    text.push_str(token.text());
-                    for _ in 1..n_input_tokens {
-                        let token = tokens.next().unwrap();
-                        text.push_str(token.text());
-                    }
-
-                    pos += TextSize::from(text.len() as u32);
-                    builder.token(kind, &text);
-                    text.clear();
-                }
-                parser::Step::FloatSplit { ends_in_dot: has_pseudo_dot } => {
-                    let token = tokens.next().unwrap();
-                    let text = token.text();
-
-                    match text.split_once('.') {
-                        Some((left, right)) => {
-                            assert!(!left.is_empty());
-                            builder.start_node(SyntaxKind::NAME_REF);
-                            builder.token(SyntaxKind::INT_NUMBER, left);
-                            builder.finish_node();
-
-                            // here we move the exit up, the original exit has been deleted in process
-                            builder.finish_node();
-
-                            builder.token(SyntaxKind::DOT, ".");
-
-                            if has_pseudo_dot {
-                                assert!(right.is_empty(), "{left}.{right}");
-                            } else {
-                                assert!(!right.is_empty(), "{left}.{right}");
-                                builder.start_node(SyntaxKind::NAME_REF);
-                                builder.token(SyntaxKind::INT_NUMBER, right);
-                                builder.finish_node();
-
-                                // the parser creates an unbalanced start node, we are required to close it here
-                                builder.finish_node();
-                            }
-                        }
-                        None => unreachable!(),
-                    }
-                    pos += TextSize::from(text.len() as u32);
-                }
-                parser::Step::Enter { kind } => builder.start_node(kind),
-                parser::Step::Exit => builder.finish_node(),
-                parser::Step::Error { msg } => builder.error(msg.to_owned(), pos),
-            }
-        }
-
-        let (green, errors) = builder.finish_raw();
-        Parse::new(green, errors)
-    }
-}
-
 /// Matches a `SyntaxNode` against an `ast` type.
 ///
 /// # Example:
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing.rs
index 4bf2a0327912d..2c7828c05246b 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing.rs
@@ -11,8 +11,8 @@ pub(crate) use crate::parsing::reparsing::incremental_reparse;
 
 pub(crate) fn parse_text(text: &str, edition: parser::Edition) -> (GreenNode, Vec) {
     let _p = tracing::info_span!("parse_text").entered();
-    let lexed = parser::LexedStr::new(text);
-    let parser_input = lexed.to_input();
+    let lexed = parser::LexedStr::new(edition, text);
+    let parser_input = lexed.to_input(edition);
     let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input, edition);
     let (node, errors, _eof) = build_tree(lexed, parser_output);
     (node, errors)
@@ -24,8 +24,8 @@ pub(crate) fn parse_text_at(
     edition: parser::Edition,
 ) -> (GreenNode, Vec) {
     let _p = tracing::info_span!("parse_text_at").entered();
-    let lexed = parser::LexedStr::new(text);
-    let parser_input = lexed.to_input();
+    let lexed = parser::LexedStr::new(edition, text);
+    let parser_input = lexed.to_input(edition);
     let parser_output = entry.parse(&parser_input, edition);
     let (node, errors, _eof) = build_tree(lexed, parser_output);
     (node, errors)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
index 354b89fd49032..a5cc4e90dfbc1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
@@ -6,7 +6,7 @@
 //!   - otherwise, we search for the nearest `{}` block which contains the edit
 //!     and try to parse only this block.
 
-use parser::Reparser;
+use parser::{Edition, Reparser};
 use text_edit::Indel;
 
 use crate::{
@@ -21,14 +21,13 @@ pub(crate) fn incremental_reparse(
     node: &SyntaxNode,
     edit: &Indel,
     errors: impl IntoIterator,
+    edition: Edition,
 ) -> Option<(GreenNode, Vec, TextRange)> {
-    if let Some((green, new_errors, old_range)) = reparse_token(node, edit) {
+    if let Some((green, new_errors, old_range)) = reparse_token(node, edit, edition) {
         return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
     }
 
-    if let Some((green, new_errors, old_range)) =
-        reparse_block(node, edit, parser::Edition::CURRENT)
-    {
+    if let Some((green, new_errors, old_range)) = reparse_block(node, edit, edition) {
         return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
     }
     None
@@ -37,6 +36,7 @@ pub(crate) fn incremental_reparse(
 fn reparse_token(
     root: &SyntaxNode,
     edit: &Indel,
+    edition: Edition,
 ) -> Option<(GreenNode, Vec, TextRange)> {
     let prev_token = root.covering_element(edit.delete).as_token()?.clone();
     let prev_token_kind = prev_token.kind();
@@ -51,7 +51,7 @@ fn reparse_token(
             }
 
             let mut new_text = get_text_after_edit(prev_token.clone().into(), edit);
-            let (new_token_kind, new_err) = parser::LexedStr::single_token(&new_text)?;
+            let (new_token_kind, new_err) = parser::LexedStr::single_token(edition, &new_text)?;
 
             if new_token_kind != prev_token_kind
                 || (new_token_kind == IDENT && is_contextual_kw(&new_text))
@@ -64,7 +64,7 @@ fn reparse_token(
             // `b` no longer remains an identifier, but becomes a part of byte string literal
             if let Some(next_char) = root.text().char_at(prev_token.text_range().end()) {
                 new_text.push(next_char);
-                let token_with_next_char = parser::LexedStr::single_token(&new_text);
+                let token_with_next_char = parser::LexedStr::single_token(edition, &new_text);
                 if let Some((_kind, _error)) = token_with_next_char {
                     return None;
                 }
@@ -91,8 +91,8 @@ fn reparse_block(
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
     let text = get_text_after_edit(node.clone().into(), edit);
 
-    let lexed = parser::LexedStr::new(text.as_str());
-    let parser_input = lexed.to_input();
+    let lexed = parser::LexedStr::new(edition, text.as_str());
+    let parser_input = lexed.to_input(edition);
     if !is_balanced(&lexed) {
         return None;
     }
@@ -199,6 +199,7 @@ mod tests {
                 before.tree().syntax(),
                 &edit,
                 before.errors.as_deref().unwrap_or_default().iter().cloned(),
+                Edition::CURRENT,
             )
             .unwrap();
             assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
diff --git a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
index 35e39229894f2..f9565721dd5f9 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
@@ -16,6 +16,7 @@ base-db.workspace = true
 rustc-hash.workspace = true
 span.workspace = true
 stdx.workspace = true
+intern.workspace = true
 
 [lints]
-workspace = true
\ No newline at end of file
+workspace = true
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 e65186d377151..e1f40f5da0116 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -1,5 +1,5 @@
 //! A set of high-level utility fixture methods to use in tests.
-use std::{iter, mem, ops::Not, str::FromStr, sync};
+use std::{iter, mem, str::FromStr, sync};
 
 use base_db::{
     CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileChange,
@@ -9,12 +9,15 @@ use cfg::CfgOptions;
 use hir_expand::{
     change::ChangeWithProcMacros,
     db::ExpandDatabase,
+    files::FilePosition,
     proc_macro::{
-        ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacros,
+        ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder,
     },
+    FileRange,
 };
+use intern::Symbol;
 use rustc_hash::FxHashMap;
-use span::{Edition, FileId, FilePosition, FileRange, Span};
+use span::{Edition, EditionedFileId, FileId, Span};
 use test_utils::{
     extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
     ESCAPED_CURSOR_MARKER,
@@ -25,7 +28,7 @@ pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0);
 
 pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static {
     #[track_caller]
-    fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
+    fn with_single_file(ra_fixture: &str) -> (Self, EditionedFileId) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -34,7 +37,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static {
     }
 
     #[track_caller]
-    fn with_many_files(ra_fixture: &str) -> (Self, Vec) {
+    fn with_many_files(ra_fixture: &str) -> (Self, Vec) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -78,7 +81,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static {
     }
 
     #[track_caller]
-    fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
+    fn with_range_or_offset(ra_fixture: &str) -> (Self, EditionedFileId, RangeOrOffset) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -101,8 +104,8 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabaseExt + 'static {
 impl WithFixture for DB {}
 
 pub struct ChangeFixture {
-    pub file_position: Option<(FileId, RangeOrOffset)>,
-    pub files: Vec,
+    pub file_position: Option<(EditionedFileId, RangeOrOffset)>,
+    pub files: Vec,
     pub change: ChangeWithProcMacros,
 }
 
@@ -150,13 +153,14 @@ impl ChangeFixture {
         let mut file_position = None;
 
         for entry in fixture {
+            let mut range_or_offset = None;
             let text = if entry.text.contains(CURSOR_MARKER) {
                 if entry.text.contains(ESCAPED_CURSOR_MARKER) {
                     entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
                 } else {
-                    let (range_or_offset, text) = extract_range_or_offset(&entry.text);
+                    let (roo, text) = extract_range_or_offset(&entry.text);
                     assert!(file_position.is_none());
-                    file_position = Some((file_id, range_or_offset));
+                    range_or_offset = Some(roo);
                     text
                 }
             } else {
@@ -164,6 +168,11 @@ impl ChangeFixture {
             };
 
             let meta = FileMeta::from_fixture(entry, current_source_root_kind);
+            if let Some(range_or_offset) = range_or_offset {
+                file_position =
+                    Some((EditionedFileId::new(file_id, meta.edition), range_or_offset));
+            }
+
             assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
             if !meta.deps.is_empty() {
                 assert!(meta.krate.is_some(), "can't specify deps without naming the crate")
@@ -215,7 +224,7 @@ impl ChangeFixture {
             source_change.change_file(file_id, Some(text));
             let path = VfsPath::new_virtual_path(meta.path);
             file_set.insert(file_id, path);
-            files.push(file_id);
+            files.push(EditionedFileId::new(file_id, meta.edition));
             file_id = FileId::from_raw(file_id.index() + 1);
         }
 
@@ -237,6 +246,7 @@ impl ChangeFixture {
             for (from, to, prelude) in crate_deps {
                 let from_id = crates[&from];
                 let to_id = crates[&to];
+                let sysroot = crate_graph[to_id].origin.is_lang();
                 crate_graph
                     .add_dep(
                         from_id,
@@ -244,7 +254,7 @@ impl ChangeFixture {
                             CrateName::new(&to).unwrap(),
                             to_id,
                             prelude,
-                            false,
+                            sysroot,
                         ),
                     )
                     .unwrap();
@@ -266,7 +276,7 @@ impl ChangeFixture {
             let core_crate = crate_graph.add_crate_root(
                 core_file,
                 Edition::CURRENT,
-                Some(CrateDisplayName::from_canonical_name("core".to_owned())),
+                Some(CrateDisplayName::from_canonical_name("core")),
                 None,
                 Default::default(),
                 Default::default(),
@@ -293,7 +303,7 @@ impl ChangeFixture {
             }
         }
 
-        let mut proc_macros = ProcMacros::default();
+        let mut proc_macros = ProcMacrosBuilder::default();
         if !proc_macro_names.is_empty() {
             let proc_lib_file = file_id;
 
@@ -313,7 +323,7 @@ impl ChangeFixture {
             let proc_macros_crate = crate_graph.add_crate_root(
                 proc_lib_file,
                 Edition::CURRENT,
-                Some(CrateDisplayName::from_canonical_name("proc_macros".to_owned())),
+                Some(CrateDisplayName::from_canonical_name("proc_macros")),
                 None,
                 Default::default(),
                 Default::default(),
@@ -344,7 +354,7 @@ impl ChangeFixture {
 
         let mut change = ChangeWithProcMacros {
             source_change,
-            proc_macros: proc_macros.is_empty().not().then_some(proc_macros),
+            proc_macros: Some(proc_macros.build()),
             toolchains: Some(iter::repeat(toolchain).take(crate_graph.len()).collect()),
             target_data_layouts: Some(
                 iter::repeat(target_data_layout).take(crate_graph.len()).collect(),
@@ -369,7 +379,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
 "#
             .into(),
             ProcMacro {
-                name: "identity".into(),
+                name: Symbol::intern("identity"),
                 kind: ProcMacroKind::Attr,
                 expander: sync::Arc::new(IdentityProcMacroExpander),
                 disabled: false,
@@ -384,7 +394,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream {
 "#
             .into(),
             ProcMacro {
-                name: "DeriveIdentity".into(),
+                name: Symbol::intern("DeriveIdentity"),
                 kind: ProcMacroKind::CustomDerive,
                 expander: sync::Arc::new(IdentityProcMacroExpander),
                 disabled: false,
@@ -399,7 +409,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
 "#
             .into(),
             ProcMacro {
-                name: "input_replace".into(),
+                name: Symbol::intern("input_replace"),
                 kind: ProcMacroKind::Attr,
                 expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
                 disabled: false,
@@ -414,7 +424,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
 "#
             .into(),
             ProcMacro {
-                name: "mirror".into(),
+                name: Symbol::intern("mirror"),
                 kind: ProcMacroKind::Bang,
                 expander: sync::Arc::new(MirrorProcMacroExpander),
                 disabled: false,
@@ -429,7 +439,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
 "#
             .into(),
             ProcMacro {
-                name: "shorten".into(),
+                name: Symbol::intern("shorten"),
                 kind: ProcMacroKind::Bang,
                 expander: sync::Arc::new(ShortenProcMacroExpander),
                 disabled: false,
@@ -447,7 +457,8 @@ fn filter_test_proc_macros(
     let mut proc_macros = Vec::new();
 
     for (c, p) in proc_macro_defs {
-        if !proc_macro_names.iter().any(|name| name == &stdx::to_lower_snake_case(&p.name)) {
+        if !proc_macro_names.iter().any(|name| name == &stdx::to_lower_snake_case(p.name.as_str()))
+        {
             continue;
         }
         proc_macros.push(p);
@@ -480,9 +491,9 @@ impl FileMeta {
         let mut cfg = CfgOptions::default();
         for (k, v) in f.cfgs {
             if let Some(v) = v {
-                cfg.insert_key_value(k.into(), v.into());
+                cfg.insert_key_value(Symbol::intern(&k), Symbol::intern(&v));
             } else {
-                cfg.insert_atom(k.into());
+                cfg.insert_atom(Symbol::intern(&k));
             }
         }
 
@@ -529,7 +540,7 @@ fn parse_crate(
 
     let origin = match LangCrateOrigin::from(&*name) {
         LangCrateOrigin::Other => {
-            let name = name.clone();
+            let name = Symbol::intern(&name);
             if non_workspace_member {
                 CrateOrigin::Library { repo, name }
             } else {
@@ -640,11 +651,11 @@ impl ProcMacroExpander for ShortenProcMacroExpander {
                 Leaf::Literal(it) => {
                     // XXX Currently replaces any literals with an empty string, but supporting
                     // "shortening" other literals would be nice.
-                    it.text = "\"\"".into();
+                    it.symbol = Symbol::empty();
                 }
                 Leaf::Punct(_) => {}
                 Leaf::Ident(it) => {
-                    it.text = it.text.chars().take(1).collect();
+                    it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::());
                 }
             }
             leaf
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index d1862f7d738e7..2d615c34a3520 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -55,6 +55,7 @@
 //!     size_of: sized
 //!     sized:
 //!     slice:
+//!     str:
 //!     sync: sized
 //!     transmute:
 //!     try: infallible
@@ -1368,6 +1369,14 @@ pub mod iter {
 }
 // endregion:iterator
 
+// region:str
+pub mod str {
+    pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
+        ""
+    }
+}
+// endregion:str
+
 // region:panic
 mod panic {
     pub macro panic_2021 {
diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml
index 1311e2ddf89e9..cea1519c2dd46 100644
--- a/src/tools/rust-analyzer/crates/tt/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml
@@ -13,10 +13,14 @@ doctest = false
 
 [dependencies]
 arrayvec.workspace = true
-smol_str.workspace = true
 text-size.workspace = true
 
 stdx.workspace = true
+intern.workspace = true
+ra-ap-rustc_lexer.workspace = true
+
+[features]
+in-rust-tree = []
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/tt/src/buffer.rs b/src/tools/rust-analyzer/crates/tt/src/buffer.rs
index cd41af03c613e..1319739371ff6 100644
--- a/src/tools/rust-analyzer/crates/tt/src/buffer.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/buffer.rs
@@ -134,6 +134,15 @@ pub enum TokenTreeRef<'a, Span> {
     Leaf(&'a Leaf, &'a TokenTree),
 }
 
+impl<'a, Span: Copy> TokenTreeRef<'a, Span> {
+    pub fn span(&self) -> Span {
+        match self {
+            TokenTreeRef::Subtree(subtree, _) => subtree.delimiter.open,
+            TokenTreeRef::Leaf(leaf, _) => *leaf.span(),
+        }
+    }
+}
+
 impl TokenTreeRef<'_, Span> {
     pub fn cloned(&self) -> TokenTree {
         match self {
diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs
index 175259a3e4719..e96bed0319e12 100644
--- a/src/tools/rust-analyzer/crates/tt/src/iter.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs
@@ -2,6 +2,7 @@
 //! macro definition into a list of patterns and templates.
 
 use arrayvec::ArrayVec;
+use intern::sym;
 
 use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree};
 
@@ -58,7 +59,7 @@ impl<'a, S: Copy> TtIter<'a, S> {
 
     pub fn expect_ident(&mut self) -> Result<&'a Ident, ()> {
         match self.expect_leaf()? {
-            Leaf::Ident(it) if it.text != "_" => Ok(it),
+            Leaf::Ident(it) if it.sym != sym::underscore => Ok(it),
             _ => Err(()),
         }
     }
@@ -74,7 +75,7 @@ impl<'a, S: Copy> TtIter<'a, S> {
         let it = self.expect_leaf()?;
         match it {
             Leaf::Literal(_) => Ok(it),
-            Leaf::Ident(ident) if ident.text == "true" || ident.text == "false" => Ok(it),
+            Leaf::Ident(ident) if ident.sym == sym::true_ || ident.sym == sym::false_ => Ok(it),
             _ => Err(()),
         }
     }
@@ -142,6 +143,10 @@ impl<'a, S: Copy> TtIter<'a, S> {
         self.inner.as_slice().get(n)
     }
 
+    pub fn next_span(&self) -> Option {
+        Some(self.inner.as_slice().first()?.first_span())
+    }
+
     pub fn as_slice(&self) -> &'a [TokenTree] {
         self.inner.as_slice()
     }
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index 369744d0e96c7..7b72f9ff108d3 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -2,18 +2,74 @@
 //! input and output) of macros. It closely mirrors `proc_macro` crate's
 //! `TokenTree`.
 
+#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
+
+#[cfg(not(feature = "in-rust-tree"))]
+extern crate ra_ap_rustc_lexer as rustc_lexer;
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_lexer;
+
 pub mod buffer;
 pub mod iter;
 
 use std::fmt;
 
-use stdx::impl_from;
+use intern::Symbol;
+use stdx::{impl_from, itertools::Itertools as _};
 
-pub use smol_str::SmolStr;
 pub use text_size::{TextRange, TextSize};
 
+#[derive(Clone, PartialEq, Debug)]
+pub struct Lit {
+    pub kind: LitKind,
+    pub symbol: Symbol,
+    pub suffix: Option,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum IdentIsRaw {
+    No,
+    Yes,
+}
+impl IdentIsRaw {
+    pub fn yes(self) -> bool {
+        matches!(self, IdentIsRaw::Yes)
+    }
+    pub fn no(&self) -> bool {
+        matches!(self, IdentIsRaw::No)
+    }
+    pub fn as_str(self) -> &'static str {
+        match self {
+            IdentIsRaw::No => "",
+            IdentIsRaw::Yes => "r#",
+        }
+    }
+    pub fn split_from_symbol(sym: &str) -> (Self, &str) {
+        if let Some(sym) = sym.strip_prefix("r#") {
+            (IdentIsRaw::Yes, sym)
+        } else {
+            (IdentIsRaw::No, sym)
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+pub enum LitKind {
+    Byte,
+    Char,
+    Integer, // e.g. `1`, `1u8`, `1f32`
+    Float,   // e.g. `1.`, `1.0`, `1e3f32`
+    Str,
+    StrRaw(u8), // raw string delimited by `n` hash symbols
+    ByteStr,
+    ByteStrRaw(u8), // raw byte string delimited by `n` hash symbols
+    CStr,
+    CStrRaw(u8),
+    Err(()),
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum TokenTree {
+pub enum TokenTree {
     Leaf(Leaf),
     Subtree(Subtree),
 }
@@ -103,6 +159,15 @@ pub struct DelimSpan {
     pub close: S,
 }
 
+impl DelimSpan {
+    pub fn from_single(sp: Span) -> Self {
+        DelimSpan { open: sp, close: sp }
+    }
+
+    pub fn from_pair(open: Span, close: Span) -> Self {
+        DelimSpan { open, close }
+    }
+}
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct Delimiter {
     pub open: S,
@@ -134,8 +199,66 @@ pub enum DelimiterKind {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Literal {
-    pub text: SmolStr,
+    // escaped
+    pub symbol: Symbol,
     pub span: S,
+    pub kind: LitKind,
+    pub suffix: Option,
+}
+
+pub fn token_to_literal(text: &str, span: S) -> Literal
+where
+    S: Copy,
+{
+    use rustc_lexer::LiteralKind;
+
+    let token = rustc_lexer::tokenize(text).next_tuple();
+    let Some((rustc_lexer::Token {
+        kind: rustc_lexer::TokenKind::Literal { kind, suffix_start },
+        ..
+    },)) = token
+    else {
+        return Literal {
+            span,
+            symbol: Symbol::intern(text),
+            kind: LitKind::Err(()),
+            suffix: None,
+        };
+    };
+
+    let (kind, start_offset, end_offset) = match kind {
+        LiteralKind::Int { .. } => (LitKind::Integer, 0, 0),
+        LiteralKind::Float { .. } => (LitKind::Float, 0, 0),
+        LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize),
+        LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize),
+        LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize),
+        LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize),
+        LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize),
+        LiteralKind::RawStr { n_hashes } => (
+            LitKind::StrRaw(n_hashes.unwrap_or_default()),
+            2 + n_hashes.unwrap_or_default() as usize,
+            1 + n_hashes.unwrap_or_default() as usize,
+        ),
+        LiteralKind::RawByteStr { n_hashes } => (
+            LitKind::ByteStrRaw(n_hashes.unwrap_or_default()),
+            3 + n_hashes.unwrap_or_default() as usize,
+            1 + n_hashes.unwrap_or_default() as usize,
+        ),
+        LiteralKind::RawCStr { n_hashes } => (
+            LitKind::CStrRaw(n_hashes.unwrap_or_default()),
+            3 + n_hashes.unwrap_or_default() as usize,
+            1 + n_hashes.unwrap_or_default() as usize,
+        ),
+    };
+
+    let (lit, suffix) = text.split_at(suffix_start as usize);
+    let lit = &lit[start_offset..lit.len() - end_offset];
+    let suffix = match suffix {
+        "" | "_" => None,
+        suffix => Some(Symbol::intern(suffix)),
+    };
+
+    Literal { span, symbol: Symbol::intern(lit), kind, suffix }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -145,23 +268,79 @@ pub struct Punct {
     pub span: S,
 }
 
+/// Indicates whether a token can join with the following token to form a
+/// compound token. Used for conversions to `proc_macro::Spacing`. Also used to
+/// guide pretty-printing, which is where the `JointHidden` value (which isn't
+/// part of `proc_macro::Spacing`) comes in useful.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum Spacing {
+    /// The token cannot join with the following token to form a compound
+    /// token.
+    ///
+    /// In token streams parsed from source code, the compiler will use `Alone`
+    /// for any token immediately followed by whitespace, a non-doc comment, or
+    /// EOF.
+    ///
+    /// When constructing token streams within the compiler, use this for each
+    /// token that (a) should be pretty-printed with a space after it, or (b)
+    /// is the last token in the stream. (In the latter case the choice of
+    /// spacing doesn't matter because it is never used for the last token. We
+    /// arbitrarily use `Alone`.)
+    ///
+    /// Converts to `proc_macro::Spacing::Alone`, and
+    /// `proc_macro::Spacing::Alone` converts back to this.
     Alone,
-    /// Whether the following token is joint to this one.
+
+    /// The token can join with the following token to form a compound token.
+    ///
+    /// In token streams parsed from source code, the compiler will use `Joint`
+    /// for any token immediately followed by punctuation (as determined by
+    /// `Token::is_punct`).
+    ///
+    /// When constructing token streams within the compiler, use this for each
+    /// token that (a) should be pretty-printed without a space after it, and
+    /// (b) is followed by a punctuation token.
+    ///
+    /// Converts to `proc_macro::Spacing::Joint`, and
+    /// `proc_macro::Spacing::Joint` converts back to this.
     Joint,
+
+    /// The token can join with the following token to form a compound token,
+    /// but this will not be visible at the proc macro level. (This is what the
+    /// `Hidden` means; see below.)
+    ///
+    /// In token streams parsed from source code, the compiler will use
+    /// `JointHidden` for any token immediately followed by anything not
+    /// covered by the `Alone` and `Joint` cases: an identifier, lifetime,
+    /// literal, delimiter, doc comment.
+    ///
+    /// When constructing token streams, use this for each token that (a)
+    /// should be pretty-printed without a space after it, and (b) is followed
+    /// by a non-punctuation token.
+    ///
+    /// Converts to `proc_macro::Spacing::Alone`, but
+    /// `proc_macro::Spacing::Alone` converts back to `token::Spacing::Alone`.
+    /// Because of that, pretty-printing of `TokenStream`s produced by proc
+    /// macros is unavoidably uglier (with more whitespace between tokens) than
+    /// pretty-printing of `TokenStream`'s produced by other means (i.e. parsed
+    /// source code, internally constructed token streams, and token streams
+    /// produced by declarative macros).
+    JointHidden,
 }
 
+/// Identifier or keyword.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-/// Identifier or keyword. Unlike rustc, we keep "r#" prefix when it represents a raw identifier.
 pub struct Ident {
-    pub text: SmolStr,
+    pub sym: Symbol,
     pub span: S,
+    pub is_raw: IdentIsRaw,
 }
 
 impl Ident {
-    pub fn new(text: impl Into, span: S) -> Self {
-        Ident { text: text.into(), span }
+    pub fn new(text: &str, span: S) -> Self {
+        // let raw_stripped = IdentIsRaw::split_from_symbol(text.as_ref());
+        let (is_raw, text) = IdentIsRaw::split_from_symbol(text);
+        Ident { sym: Symbol::intern(text), span, is_raw }
     }
 }
 
@@ -207,22 +386,35 @@ fn print_debug_token(
     match tkn {
         TokenTree::Leaf(leaf) => match leaf {
             Leaf::Literal(lit) => {
-                write!(f, "{}LITERAL {}", align, lit.text)?;
-                fmt::Debug::fmt(&lit.span, f)?;
+                write!(
+                    f,
+                    "{}LITERAL {:?} {}{} {:#?}",
+                    align,
+                    lit.kind,
+                    lit.symbol,
+                    lit.suffix.as_ref().map(|it| it.as_str()).unwrap_or(""),
+                    lit.span
+                )?;
             }
             Leaf::Punct(punct) => {
                 write!(
                     f,
-                    "{}PUNCH   {} [{}] ",
+                    "{}PUNCH   {} [{}] {:#?}",
                     align,
                     punct.char,
                     if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
+                    punct.span
                 )?;
-                fmt::Debug::fmt(&punct.span, f)?;
             }
             Leaf::Ident(ident) => {
-                write!(f, "{}IDENT   {} ", align, ident.text)?;
-                fmt::Debug::fmt(&ident.span, f)?;
+                write!(
+                    f,
+                    "{}IDENT   {}{} {:#?}",
+                    align,
+                    ident.is_raw.as_str(),
+                    ident.sym,
+                    ident.span
+                )?;
             }
         },
         TokenTree::Subtree(subtree) => {
@@ -288,13 +480,52 @@ impl fmt::Display for Leaf {
 
 impl fmt::Display for Ident {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.text, f)
+        fmt::Display::fmt(&self.is_raw.as_str(), f)?;
+        fmt::Display::fmt(&self.sym, f)
     }
 }
 
 impl fmt::Display for Literal {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.text, f)
+        match self.kind {
+            LitKind::Byte => write!(f, "b'{}'", self.symbol),
+            LitKind::Char => write!(f, "'{}'", self.symbol),
+            LitKind::Integer | LitKind::Float | LitKind::Err(_) => write!(f, "{}", self.symbol),
+            LitKind::Str => write!(f, "\"{}\"", self.symbol),
+            LitKind::ByteStr => write!(f, "b\"{}\"", self.symbol),
+            LitKind::CStr => write!(f, "c\"{}\"", self.symbol),
+            LitKind::StrRaw(num_of_hashes) => {
+                let num_of_hashes = num_of_hashes as usize;
+                write!(
+                    f,
+                    r#"r{0:# {
+                let num_of_hashes = num_of_hashes as usize;
+                write!(
+                    f,
+                    r#"br{0:# {
+                let num_of_hashes = num_of_hashes as usize;
+                write!(
+                    f,
+                    r#"cr{0:# Subtree {
             let s = match child {
                 TokenTree::Leaf(it) => {
                     let s = match it {
-                        Leaf::Literal(it) => it.text.to_string(),
+                        Leaf::Literal(it) => it.symbol.to_string(),
                         Leaf::Punct(it) => it.char.to_string(),
-                        Leaf::Ident(it) => it.text.to_string(),
+                        Leaf::Ident(it) => format!("{}{}", it.is_raw.as_str(), it.sym),
                     };
                     match (it, last) {
                         (Leaf::Ident(_), Some(&TokenTree::Leaf(Leaf::Ident(_)))) => {
@@ -369,8 +600,10 @@ impl Subtree {
 pub fn pretty(tkns: &[TokenTree]) -> String {
     fn tokentree_to_text(tkn: &TokenTree) -> String {
         match tkn {
-            TokenTree::Leaf(Leaf::Ident(ident)) => ident.text.clone().into(),
-            TokenTree::Leaf(Leaf::Literal(literal)) => literal.text.clone().into(),
+            TokenTree::Leaf(Leaf::Ident(ident)) => {
+                format!("{}{}", ident.is_raw.as_str(), ident.sym)
+            }
+            TokenTree::Leaf(Leaf::Literal(literal)) => literal.symbol.as_str().to_owned(),
             TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char),
             TokenTree::Subtree(subtree) => {
                 let content = pretty(&subtree.token_trees);
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index b3aa6e2fe1138..77f890fd7e0de 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -67,16 +67,16 @@ pub struct FileId(u32);
 // pub struct FileId(NonMaxU32);
 
 impl FileId {
-    pub const MAX_FILE_ID: u32 = 0x7fff_ffff;
+    pub const MAX: u32 = 0x7fff_ffff;
 
     #[inline]
     pub const fn from_raw(raw: u32) -> FileId {
-        assert!(raw <= Self::MAX_FILE_ID);
+        assert!(raw <= Self::MAX);
         FileId(raw)
     }
 
     #[inline]
-    pub fn index(self) -> u32 {
+    pub const fn index(self) -> u32 {
         self.0
     }
 }
diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md
index f4e7263868c4b..4f8723a936891 100644
--- a/src/tools/rust-analyzer/docs/dev/architecture.md
+++ b/src/tools/rust-analyzer/docs/dev/architecture.md
@@ -408,7 +408,7 @@ It has a much richer vocabulary of types than `ide`, but the basic testing setup
 For comparisons, we use the `expect` crate for snapshot testing.
 
 To test various analysis corner cases and avoid forgetting about old tests, we use so-called marks.
-See the `marks` module in the `test_utils` crate for more.
+See the [cov_mark](https://docs.rs/cov-mark/latest/cov_mark/) crate documentation for more.
 
 **Architecture Invariant:** rust-analyzer tests do not use libcore or libstd.
 All required library code must be a part of the tests.
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index 74acb6f9940be..e559f88e23353 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 @@
  remapped_path/remap-path-prefix-lint.rs:9:5
+   |
+LL | /// 
+   |     ^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> remapped_path/remap-path-prefix-lint.rs:7:9
+   |
+LL | #![deny(rustdoc::invalid_html_tags)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
new file mode 100644
index 0000000000000..5d0881029cb2f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
@@ -0,0 +1,2 @@
+//@ build-aux-docs
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
new file mode 100644
index 0000000000000..fab9ec4a92b96
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
@@ -0,0 +1,4 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
new file mode 100644
index 0000000000000..85c460ace642f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
@@ -0,0 +1,16 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+//@ has q/struct.Quebec.html
+//@ has s/struct.Sierra.html
+//@ has t/trait.Tango.html
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ hasraw search-index.js 'Tango'
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Quebec'
+
+// We document multiple crates into the same output directory, which
+// merges the cross-crate information. Everything is available.
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
new file mode 100644
index 0000000000000..932a0b17206d6
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
@@ -0,0 +1,5 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
new file mode 100644
index 0000000000000..c21a59c65188f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
@@ -0,0 +1,7 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
new file mode 100644
index 0000000000000..68bfc34883bd8
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
@@ -0,0 +1,24 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ has index.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
+//@ has index.html '//ul[@class="all-items"]//a[@href="s/index.html"]' 's'
+//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has q/struct.Quebec.html
+//@ has s/struct.Sierra.html
+//@ has t/trait.Tango.html
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ hasraw search-index.js 'Tango'
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Quebec'
+
+// We document multiple crates into the same output directory, which
+// merges the cross-crate information. Everything is available.
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
new file mode 100644
index 0000000000000..abc580a388cd2
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
@@ -0,0 +1,2 @@
+//@ build-aux-docs
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
new file mode 100644
index 0000000000000..c93298f969eab
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
@@ -0,0 +1,14 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+//@ has e/enum.Echo.html
+//@ has f/trait.Foxtrot.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
+
+// document two crates in the same way that cargo does. do not provide
+// --enable-index-page
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
new file mode 100644
index 0000000000000..a2a7033b13112
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
@@ -0,0 +1,5 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/e.rs b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
new file mode 100644
index 0000000000000..00f86cbc34889
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
@@ -0,0 +1,21 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ has index.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
+//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
+//@ has e/enum.Echo.html
+//@ has f/trait.Foxtrot.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
+
+// document two crates in the same way that cargo does, writing them both
+// into the same output directory
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
new file mode 100644
index 0000000000000..abc580a388cd2
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
@@ -0,0 +1,2 @@
+//@ build-aux-docs
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/e.rs b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
new file mode 100644
index 0000000000000..ffee898cd966d
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
@@ -0,0 +1,20 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ has index.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
+//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
+//@ has e/enum.Echo.html
+//@ has f/trait.Foxtrot.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
+
+// only declare --enable-index-page to the last rustdoc invocation
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
new file mode 100644
index 0000000000000..932a0b17206d6
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
@@ -0,0 +1,5 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
new file mode 100644
index 0000000000000..2c0db2abc53d9
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
@@ -0,0 +1,7 @@
+//@ aux-build:s.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+extern crate s;
+pub type Romeo = s::Sierra;
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
new file mode 100644
index 0000000000000..355d3f1aaa883
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
@@ -0,0 +1,8 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
new file mode 100644
index 0000000000000..c21a59c65188f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
@@ -0,0 +1,7 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
new file mode 100644
index 0000000000000..bcb9464795af2
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
@@ -0,0 +1,30 @@
+//@ aux-build:r.rs
+//@ aux-build:q.rs
+//@ aux-build:t.rs
+//@ aux-build:s.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html
+//@ has index.html '//ul[@class="all-items"]//a[@href="i/index.html"]' 'i'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
+//@ has index.html '//ul[@class="all-items"]//a[@href="r/index.html"]' 'r'
+//@ has index.html '//ul[@class="all-items"]//a[@href="s/index.html"]' 's'
+//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has q/struct.Quebec.html
+//@ has r/type.Romeo.html
+//@ has s/struct.Sierra.html
+//@ has t/trait.Tango.html
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ hasraw search-index.js 'Quebec'
+//@ hasraw search-index.js 'Romeo'
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Tango'
+//@ has type.impl/s/struct.Sierra.js
+//@ hasraw type.impl/s/struct.Sierra.js 'Tango'
+//@ hasraw type.impl/s/struct.Sierra.js 'Romeo'
+
+// document everything in the default mode
diff --git a/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
new file mode 100644
index 0000000000000..c5e3dc0a0f4e5
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
@@ -0,0 +1,12 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ has index.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
+//@ has q/struct.Quebec.html
+//@ hasraw search-index.js 'Quebec'
+
+// there's nothing cross-crate going on here
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
new file mode 100644
index 0000000000000..d3e71fa0ce35f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
@@ -0,0 +1,6 @@
+//@ build-aux-docs
+//@ has q/struct.Quebec.html
+//@ hasraw search-index.js 'Quebec'
+
+// there's nothing cross-crate going on here
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
new file mode 100644
index 0000000000000..5d0881029cb2f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
@@ -0,0 +1,2 @@
+//@ build-aux-docs
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
new file mode 100644
index 0000000000000..fab9ec4a92b96
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
@@ -0,0 +1,4 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/transitive/s.rs b/tests/rustdoc/cross-crate-info/transitive/s.rs
new file mode 100644
index 0000000000000..0a4e5f646ddaa
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/transitive/s.rs
@@ -0,0 +1,6 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+// simple test to see if we support building transitive crates
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
new file mode 100644
index 0000000000000..abc580a388cd2
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
@@ -0,0 +1,2 @@
+//@ build-aux-docs
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/two/e.rs b/tests/rustdoc/cross-crate-info/two/e.rs
new file mode 100644
index 0000000000000..9665af62706d1
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/two/e.rs
@@ -0,0 +1,6 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+// simple test to assert that we can do a two-level aux-build
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
new file mode 100644
index 0000000000000..a7ab062fd9e28
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
@@ -0,0 +1,10 @@
+//@ build-aux-docs
+//@ doc-flags:--scrape-examples-output-path=examples
+//@ doc-flags:--scrape-examples-target-crate=q
+//@ doc-flags:-Zunstable-options
+
+//@ has examples
+
+// where will --scrape-examples-output-path resolve the path to be?
+// should be the root output directory
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
new file mode 100644
index 0000000000000..f8c9adcaf9cad
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
@@ -0,0 +1,3 @@
+//@ build-aux-docs
+//@ unique-doc-out-dir
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
new file mode 100644
index 0000000000000..9dcec211e1787
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
@@ -0,0 +1,14 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+//@ has e/enum.Echo.html
+//@ !has f/trait.Foxtrot.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ !hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
+
+// test the fact that our test runner will document this crate somewhere
+// else
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/type-alias/impl_trait_in_assoc_type.rs b/tests/rustdoc/type-alias/impl_trait_in_assoc_type.rs
new file mode 100644
index 0000000000000..2846710cbed7c
--- /dev/null
+++ b/tests/rustdoc/type-alias/impl_trait_in_assoc_type.rs
@@ -0,0 +1,17 @@
+#![feature(impl_trait_in_assoc_type)]
+
+pub struct AlwaysTrue;
+
+//@ has impl_trait_in_assoc_type/struct.AlwaysTrue.html
+
+impl IntoIterator for AlwaysTrue {
+    type Item = bool;
+
+    //@ has - '//*[@id="associatedtype.IntoIter"]//h4[@class="code-header"]' \
+    //  'type IntoIter = impl Iterator'
+    type IntoIter = impl Iterator;
+
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::repeat(true)
+    }
+}
diff --git a/tests/rustdoc/unsafe-extern-blocks.rs b/tests/rustdoc/unsafe-extern-blocks.rs
index 22d3beea6c3a9..829095f300f20 100644
--- a/tests/rustdoc/unsafe-extern-blocks.rs
+++ b/tests/rustdoc/unsafe-extern-blocks.rs
@@ -1,6 +1,5 @@
 // Test to ensure the feature is working as expected.
 
-#![feature(unsafe_extern_blocks)]
 #![crate_name = "foo"]
 
 // @has 'foo/index.html'
@@ -13,7 +12,7 @@
 // @count - '//ul[@class="item-table"]//sup[@title="unsafe function"]' 1
 // @has - '//ul[@class="item-table"]//sup[@title="unsafe function"]' '⚠'
 
-unsafe extern {
+unsafe extern "C" {
     // @has 'foo/static.FOO.html'
     // @has - '//pre[@class="rust item-decl"]' 'pub static FOO: i32'
     pub safe static FOO: i32;
diff --git a/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.rs b/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.rs
index 33e10a0071330..c761b8e94bd7b 100644
--- a/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.rs
+++ b/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.rs
@@ -1,5 +1,4 @@
 //@ compile-flags: -Z unstable-options
-//@ ignore-stage1 (can be removed after beta bump, #[cfg(bootstrap)])
 #![feature(rustc_private)]
 #![deny(rustc::non_glob_import_of_type_ir_inherent)]
 
diff --git a/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.stderr b/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.stderr
index 84e3867c95bc1..c5c10bb070a31 100644
--- a/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.stderr
+++ b/tests/ui-fulldeps/internal-lints/non_glob_import_of_type_ir_inherent.stderr
@@ -1,5 +1,5 @@
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:17:9
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:16:9
    |
 LL |     use rustc_type_ir::inherent::Predicate;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^---------
@@ -7,25 +7,25 @@ LL |     use rustc_type_ir::inherent::Predicate;
    |                                  help: try using a glob import instead: `*`
    |
 note: the lint level is defined here
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:4:9
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:3:9
    |
 LL | #![deny(rustc::non_glob_import_of_type_ir_inherent)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:18:35
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:17:35
    |
 LL |     use rustc_type_ir::inherent::{AdtDef, Ty};
    |                                   ^^^^^^ help: try using a glob import instead: `*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:18:43
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:17:43
    |
 LL |     use rustc_type_ir::inherent::{AdtDef, Ty};
    |                                           ^^ help: try using a glob import instead: `*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:21:9
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:20:9
    |
 LL |     use rustc_type_ir::inherent::ParamEnv as _;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^-------------
@@ -33,31 +33,31 @@ LL |     use rustc_type_ir::inherent::ParamEnv as _;
    |                                  help: try using a glob import instead: `*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:25:9
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:24:9
    |
 LL |     use rustc_type_ir::inherent;
    |         ^^^^^^^^^^^^^^^^^^^^^^^- help: try using a glob import instead: `::*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:26:9
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:25:9
    |
 LL |     use rustc_type_ir::inherent as inh;
    |         ^^^^^^^^^^^^^^^^^^^^^^^------- help: try using a glob import instead: `::*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:27:25
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:26:25
    |
 LL |     use rustc_type_ir::{inherent as _};
    |                         ^^^^^^^^----- help: try using a glob import instead: `::*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:34:35
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:33:35
    |
 LL |     use rustc_type_ir::inherent::{self};
    |                                   ^^^^ help: try using a glob import instead: `*`
 
 error: non-glob import of `rustc_type_ir::inherent`
-  --> $DIR/non_glob_import_of_type_ir_inherent.rs:35:35
+  --> $DIR/non_glob_import_of_type_ir_inherent.rs:34:35
    |
 LL |     use rustc_type_ir::inherent::{self as innate};
    |                                   ^^^^----------
diff --git a/tests/crashes/121444.rs b/tests/ui/abi/large-byval-align.rs
similarity index 68%
rename from tests/crashes/121444.rs
rename to tests/ui/abi/large-byval-align.rs
index a6373a58c426e..e39170df72b49 100644
--- a/tests/crashes/121444.rs
+++ b/tests/ui/abi/large-byval-align.rs
@@ -1,11 +1,13 @@
-//@ known-bug: #121444
 //@ compile-flags: -Copt-level=0
-//@ edition:2021
 //@ only-x86_64
 //@ ignore-windows
+//@ min-llvm-version: 19
+//@ build-pass
+
 #[repr(align(536870912))]
 pub struct A(i64);
 
+#[allow(improper_ctypes_definitions)]
 pub extern "C" fn foo(x: A) {}
 
 fn main() {
diff --git a/tests/ui/asm/aarch64/type-check-2.rs b/tests/ui/asm/aarch64/type-check-2.rs
index ba68cdd26d94c..46667ae3a6565 100644
--- a/tests/ui/asm/aarch64/type-check-2.rs
+++ b/tests/ui/asm/aarch64/type-check-2.rs
@@ -15,15 +15,6 @@ fn main() {
     unsafe {
         // Inputs must be initialized
 
-        // Sym operands must point to a function or static
-
-        const C: i32 = 0;
-        static S: i32 = 0;
-        asm!("{}", sym S);
-        asm!("{}", sym main);
-        asm!("{}", sym C);
-        //~^ ERROR invalid `sym` operand
-
         // Register operands must be Copy
 
         asm!("{:v}", in(vreg) SimdNonCopy(0.0, 0.0, 0.0, 0.0));
@@ -65,12 +56,3 @@ fn main() {
         asm!("{}", in(reg) u);
     }
 }
-
-// Sym operands must point to a function or static
-
-const C: i32 = 0;
-static S: i32 = 0;
-global_asm!("{}", sym S);
-global_asm!("{}", sym main);
-global_asm!("{}", sym C);
-//~^ ERROR invalid `sym` operand
diff --git a/tests/ui/asm/aarch64/type-check-2.stderr b/tests/ui/asm/aarch64/type-check-2.stderr
index d647f6a9f0635..b7723fc74d4b6 100644
--- a/tests/ui/asm/aarch64/type-check-2.stderr
+++ b/tests/ui/asm/aarch64/type-check-2.stderr
@@ -1,29 +1,13 @@
-error: invalid `sym` operand
-  --> $DIR/type-check-2.rs:75:19
-   |
-LL | global_asm!("{}", sym C);
-   |                   ^^^^^ is an `i32`
-   |
-   = help: `sym` operands must refer to either a function or a static
-
-error: invalid `sym` operand
-  --> $DIR/type-check-2.rs:24:20
-   |
-LL |         asm!("{}", sym C);
-   |                    ^^^^^ is an `i32`
-   |
-   = help: `sym` operands must refer to either a function or a static
-
 error: arguments for inline assembly must be copyable
-  --> $DIR/type-check-2.rs:29:31
+  --> $DIR/type-check-2.rs:20:31
    |
 LL |         asm!("{:v}", in(vreg) SimdNonCopy(0.0, 0.0, 0.0, 0.0));
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `SimdNonCopy` does not implement the Copy trait
 
-error: cannot use value of type `{closure@$DIR/type-check-2.rs:41:28: 41:36}` for inline assembly
-  --> $DIR/type-check-2.rs:41:28
+error: cannot use value of type `{closure@$DIR/type-check-2.rs:32:28: 32:36}` for inline assembly
+  --> $DIR/type-check-2.rs:32:28
    |
 LL |         asm!("{}", in(reg) |x: i32| x);
    |                            ^^^^^^^^^^
@@ -31,7 +15,7 @@ LL |         asm!("{}", in(reg) |x: i32| x);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `Vec` for inline assembly
-  --> $DIR/type-check-2.rs:43:28
+  --> $DIR/type-check-2.rs:34:28
    |
 LL |         asm!("{}", in(reg) vec![0]);
    |                            ^^^^^^^
@@ -40,7 +24,7 @@ LL |         asm!("{}", in(reg) vec![0]);
    = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: cannot use value of type `(i32, i32, i32)` for inline assembly
-  --> $DIR/type-check-2.rs:45:28
+  --> $DIR/type-check-2.rs:36:28
    |
 LL |         asm!("{}", in(reg) (1, 2, 3));
    |                            ^^^^^^^^^
@@ -48,7 +32,7 @@ LL |         asm!("{}", in(reg) (1, 2, 3));
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `[i32; 3]` for inline assembly
-  --> $DIR/type-check-2.rs:47:28
+  --> $DIR/type-check-2.rs:38:28
    |
 LL |         asm!("{}", in(reg) [1, 2, 3]);
    |                            ^^^^^^^^^
@@ -56,7 +40,7 @@ LL |         asm!("{}", in(reg) [1, 2, 3]);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `fn() {main}` for inline assembly
-  --> $DIR/type-check-2.rs:55:31
+  --> $DIR/type-check-2.rs:46:31
    |
 LL |         asm!("{}", inout(reg) f);
    |                               ^
@@ -64,12 +48,12 @@ LL |         asm!("{}", inout(reg) f);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `&mut i32` for inline assembly
-  --> $DIR/type-check-2.rs:58:31
+  --> $DIR/type-check-2.rs:49:31
    |
 LL |         asm!("{}", inout(reg) r);
    |                               ^
    |
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
-error: aborting due to 9 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs
new file mode 100644
index 0000000000000..eff335ff6aaa3
--- /dev/null
+++ b/tests/ui/asm/invalid-const-operand.rs
@@ -0,0 +1,51 @@
+//@ needs-asm-support
+//@ ignore-nvptx64
+//@ ignore-spirv
+
+#![feature(asm_const)]
+
+use std::arch::{asm, global_asm};
+
+// Const operands must be integers and must be constants.
+
+global_asm!("{}", const 0);
+global_asm!("{}", const 0i32);
+global_asm!("{}", const 0i128);
+global_asm!("{}", const 0f32);
+//~^ ERROR invalid type for `const` operand
+global_asm!("{}", const 0 as *mut u8);
+//~^ ERROR invalid type for `const` operand
+
+fn main() {
+    unsafe {
+        // Const operands must be integers and must be constants.
+
+        asm!("{}", const 0);
+        asm!("{}", const 0i32);
+        asm!("{}", const 0i128);
+        asm!("{}", const 0f32);
+        //~^ ERROR invalid type for `const` operand
+        asm!("{}", const 0 as *mut u8);
+        //~^ ERROR invalid type for `const` operand
+        asm!("{}", const &0);
+        //~^ ERROR invalid type for `const` operand
+
+        // Constants must be... constant
+
+        let x = 0;
+        const fn const_foo(x: i32) -> i32 {
+            x
+        }
+        const fn const_bar(x: T) -> T {
+            x
+        }
+        asm!("{}", const x);
+        //~^ ERROR attempt to use a non-constant value in a constant
+        asm!("{}", const const_foo(0));
+        asm!("{}", const const_foo(x));
+        //~^ ERROR attempt to use a non-constant value in a constant
+        asm!("{}", const const_bar(0));
+        asm!("{}", const const_bar(x));
+        //~^ ERROR attempt to use a non-constant value in a constant
+    }
+}
diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr
new file mode 100644
index 0000000000000..a6d742b53c217
--- /dev/null
+++ b/tests/ui/asm/invalid-const-operand.stderr
@@ -0,0 +1,86 @@
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/invalid-const-operand.rs:42:26
+   |
+LL |         asm!("{}", const x);
+   |                          ^ non-constant value
+   |
+help: consider using `const` instead of `let`
+   |
+LL |         const x: /* Type */ = 0;
+   |         ~~~~~  ++++++++++++
+
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/invalid-const-operand.rs:45:36
+   |
+LL |         asm!("{}", const const_foo(x));
+   |                                    ^ non-constant value
+   |
+help: consider using `const` instead of `let`
+   |
+LL |         const x: /* Type */ = 0;
+   |         ~~~~~  ++++++++++++
+
+error[E0435]: attempt to use a non-constant value in a constant
+  --> $DIR/invalid-const-operand.rs:48:36
+   |
+LL |         asm!("{}", const const_bar(x));
+   |                                    ^ non-constant value
+   |
+help: consider using `const` instead of `let`
+   |
+LL |         const x: /* Type */ = 0;
+   |         ~~~~~  ++++++++++++
+
+error: invalid type for `const` operand
+  --> $DIR/invalid-const-operand.rs:14:19
+   |
+LL | global_asm!("{}", const 0f32);
+   |                   ^^^^^^----
+   |                         |
+   |                         is an `f32`
+   |
+   = help: `const` operands must be of an integer type
+
+error: invalid type for `const` operand
+  --> $DIR/invalid-const-operand.rs:16:19
+   |
+LL | global_asm!("{}", const 0 as *mut u8);
+   |                   ^^^^^^------------
+   |                         |
+   |                         is a `*mut u8`
+   |
+   = help: `const` operands must be of an integer type
+
+error: invalid type for `const` operand
+  --> $DIR/invalid-const-operand.rs:26:20
+   |
+LL |         asm!("{}", const 0f32);
+   |                    ^^^^^^----
+   |                          |
+   |                          is an `f32`
+   |
+   = help: `const` operands must be of an integer type
+
+error: invalid type for `const` operand
+  --> $DIR/invalid-const-operand.rs:28:20
+   |
+LL |         asm!("{}", const 0 as *mut u8);
+   |                    ^^^^^^------------
+   |                          |
+   |                          is a `*mut u8`
+   |
+   = help: `const` operands must be of an integer type
+
+error: invalid type for `const` operand
+  --> $DIR/invalid-const-operand.rs:30:20
+   |
+LL |         asm!("{}", const &0);
+   |                    ^^^^^^--
+   |                          |
+   |                          is a `&i32`
+   |
+   = help: `const` operands must be of an integer type
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0435`.
diff --git a/tests/ui/asm/invalid-sym-operand.rs b/tests/ui/asm/invalid-sym-operand.rs
new file mode 100644
index 0000000000000..2129c20b9681d
--- /dev/null
+++ b/tests/ui/asm/invalid-sym-operand.rs
@@ -0,0 +1,34 @@
+//@ needs-asm-support
+//@ ignore-nvptx64
+//@ ignore-spirv
+
+use std::arch::{asm, global_asm};
+
+// Sym operands must point to a function or static
+
+const C: i32 = 0;
+static S: i32 = 0;
+global_asm!("{}", sym S);
+global_asm!("{}", sym main);
+global_asm!("{}", sym C);
+//~^ ERROR invalid `sym` operand
+
+fn main() {
+    unsafe {
+        // Sym operands must point to a function or static
+
+        let x: u64 = 0;
+        const C: i32 = 0;
+        static S: i32 = 0;
+        asm!("{}", sym S);
+        asm!("{}", sym main);
+        asm!("{}", sym C);
+        //~^ ERROR invalid `sym` operand
+        asm!("{}", sym x);
+        //~^ ERROR invalid `sym` operand
+    }
+}
+
+unsafe fn generic() {
+    asm!("{}", sym generic::);
+}
diff --git a/tests/ui/asm/invalid-sym-operand.stderr b/tests/ui/asm/invalid-sym-operand.stderr
new file mode 100644
index 0000000000000..f0e6a17c25f6b
--- /dev/null
+++ b/tests/ui/asm/invalid-sym-operand.stderr
@@ -0,0 +1,26 @@
+error: invalid `sym` operand
+  --> $DIR/invalid-sym-operand.rs:27:24
+   |
+LL |         asm!("{}", sym x);
+   |                        ^ is a local variable
+   |
+   = help: `sym` operands must refer to either a function or a static
+
+error: invalid `sym` operand
+  --> $DIR/invalid-sym-operand.rs:13:19
+   |
+LL | global_asm!("{}", sym C);
+   |                   ^^^^^ is an `i32`
+   |
+   = help: `sym` operands must refer to either a function or a static
+
+error: invalid `sym` operand
+  --> $DIR/invalid-sym-operand.rs:25:20
+   |
+LL |         asm!("{}", sym C);
+   |                    ^^^^^ is an `i32`
+   |
+   = help: `sym` operands must refer to either a function or a static
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs
index 6d335ac2def1e..cb1e5c325c239 100644
--- a/tests/ui/asm/naked-functions.rs
+++ b/tests/ui/asm/naked-functions.rs
@@ -239,6 +239,9 @@ pub unsafe extern "C" fn compatible_target_feature() {
 }
 
 #[doc = "foo bar baz"]
+/// a doc comment
+// a normal comment
+#[doc(alias = "ADocAlias")]
 #[naked]
 pub unsafe extern "C" fn compatible_doc_attributes() {
     asm!("", options(noreturn, raw));
diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs
index 9dec3a1c394ef..16ae02828642d 100644
--- a/tests/ui/asm/parse-error.rs
+++ b/tests/ui/asm/parse-error.rs
@@ -146,5 +146,16 @@ global_asm!(format!("{{{}}}", 0), const FOO);
 //~^ ERROR asm template must be a string literal
 global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
 //~^ ERROR asm template must be a string literal
-global_asm!("{}", label {});
-//~^ ERROR expected operand, options, or additional template string
+
+global_asm!("{}", in(reg));
+//~^ ERROR the `in` operand cannot be used with `global_asm!`
+global_asm!("{}", out(reg));
+//~^ ERROR the `out` operand cannot be used with `global_asm!`
+global_asm!("{}", lateout(reg));
+//~^ ERROR the `lateout` operand cannot be used with `global_asm!`
+global_asm!("{}", inout(reg));
+//~^ ERROR the `inout` operand cannot be used with `global_asm!`
+global_asm!("{}", inlateout(reg));
+//~^ ERROR the `inlateout` operand cannot be used with `global_asm!`
+global_asm!("{}", label(reg));
+//~^ ERROR the `label` operand cannot be used with `global_asm!`
diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr
index e9ecd712bc367..f5f8d537d86b8 100644
--- a/tests/ui/asm/parse-error.stderr
+++ b/tests/ui/asm/parse-error.stderr
@@ -380,11 +380,41 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: expected operand, options, or additional template string
-  --> $DIR/parse-error.rs:149:19
+error: the `in` operand cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:150:19
+   |
+LL | global_asm!("{}", in(reg));
+   |                   ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it
+
+error: the `out` operand cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:152:19
+   |
+LL | global_asm!("{}", out(reg));
+   |                   ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it
+
+error: the `lateout` operand cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:154:19
+   |
+LL | global_asm!("{}", lateout(reg));
+   |                   ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it
+
+error: the `inout` operand cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:156:19
+   |
+LL | global_asm!("{}", inout(reg));
+   |                   ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it
+
+error: the `inlateout` operand cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:158:19
+   |
+LL | global_asm!("{}", inlateout(reg));
+   |                   ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it
+
+error: the `label` operand cannot be used with `global_asm!`
+  --> $DIR/parse-error.rs:160:19
    |
-LL | global_asm!("{}", label {});
-   |                   ^^^^^^^^ expected operand, options, or additional template string
+LL | global_asm!("{}", label(reg));
+   |                   ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it
 
 error[E0435]: attempt to use a non-constant value in a constant
   --> $DIR/parse-error.rs:39:37
@@ -441,6 +471,6 @@ help: consider using `const` instead of `let`
 LL |     const bar: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
-error: aborting due to 67 previous errors
+error: aborting due to 72 previous errors
 
 For more information about this error, try `rustc --explain E0435`.
diff --git a/tests/ui/asm/type-check-1.rs b/tests/ui/asm/type-check-1.rs
index b0f1362f54334..22669dce280bb 100644
--- a/tests/ui/asm/type-check-1.rs
+++ b/tests/ui/asm/type-check-1.rs
@@ -28,51 +28,5 @@ fn main() {
         asm!("{}", inout(reg) v[..]);
         //~^ ERROR the size for values of type `[u64]` cannot be known at compilation time
         //~| ERROR cannot use value of type `[u64]` for inline assembly
-
-        // Constants must be... constant
-
-        let x = 0;
-        const fn const_foo(x: i32) -> i32 {
-            x
-        }
-        const fn const_bar(x: T) -> T {
-            x
-        }
-        asm!("{}", const x);
-        //~^ ERROR attempt to use a non-constant value in a constant
-        asm!("{}", const const_foo(0));
-        asm!("{}", const const_foo(x));
-        //~^ ERROR attempt to use a non-constant value in a constant
-        asm!("{}", const const_bar(0));
-        asm!("{}", const const_bar(x));
-        //~^ ERROR attempt to use a non-constant value in a constant
-        asm!("{}", sym x);
-        //~^ ERROR invalid `sym` operand
-
-        // Const operands must be integers and must be constants.
-
-        asm!("{}", const 0);
-        asm!("{}", const 0i32);
-        asm!("{}", const 0i128);
-        asm!("{}", const 0f32);
-        //~^ ERROR mismatched types
-        asm!("{}", const 0 as *mut u8);
-        //~^ ERROR mismatched types
-        asm!("{}", const &0);
-        //~^ ERROR mismatched types
     }
 }
-
-unsafe fn generic() {
-    asm!("{}", sym generic::);
-}
-
-// Const operands must be integers and must be constants.
-
-global_asm!("{}", const 0);
-global_asm!("{}", const 0i32);
-global_asm!("{}", const 0i128);
-global_asm!("{}", const 0f32);
-//~^ ERROR mismatched types
-global_asm!("{}", const 0 as *mut u8);
-//~^ ERROR mismatched types
diff --git a/tests/ui/asm/type-check-1.stderr b/tests/ui/asm/type-check-1.stderr
index 1852623211813..d47e6ae1d2a97 100644
--- a/tests/ui/asm/type-check-1.stderr
+++ b/tests/ui/asm/type-check-1.stderr
@@ -1,44 +1,3 @@
-error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/type-check-1.rs:41:26
-   |
-LL |         asm!("{}", const x);
-   |                          ^ non-constant value
-   |
-help: consider using `const` instead of `let`
-   |
-LL |         const x: /* Type */ = 0;
-   |         ~~~~~  ++++++++++++
-
-error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/type-check-1.rs:44:36
-   |
-LL |         asm!("{}", const const_foo(x));
-   |                                    ^ non-constant value
-   |
-help: consider using `const` instead of `let`
-   |
-LL |         const x: /* Type */ = 0;
-   |         ~~~~~  ++++++++++++
-
-error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/type-check-1.rs:47:36
-   |
-LL |         asm!("{}", const const_bar(x));
-   |                                    ^ non-constant value
-   |
-help: consider using `const` instead of `let`
-   |
-LL |         const x: /* Type */ = 0;
-   |         ~~~~~  ++++++++++++
-
-error: invalid `sym` operand
-  --> $DIR/type-check-1.rs:49:24
-   |
-LL |         asm!("{}", sym x);
-   |                        ^ is a local variable
-   |
-   = help: `sym` operands must refer to either a function or a static
-
 error: invalid asm output
   --> $DIR/type-check-1.rs:14:29
    |
@@ -102,49 +61,6 @@ LL |         asm!("{}", inout(reg) v[..]);
    |
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
-error[E0308]: mismatched types
-  --> $DIR/type-check-1.rs:57:26
-   |
-LL |         asm!("{}", const 0f32);
-   |                          ^^^^ expected integer, found `f32`
-
-error[E0308]: mismatched types
-  --> $DIR/type-check-1.rs:59:26
-   |
-LL |         asm!("{}", const 0 as *mut u8);
-   |                          ^^^^^^^^^^^^ expected integer, found `*mut u8`
-   |
-   = note:     expected type `{integer}`
-           found raw pointer `*mut u8`
-
-error[E0308]: mismatched types
-  --> $DIR/type-check-1.rs:61:26
-   |
-LL |         asm!("{}", const &0);
-   |                          ^^ expected integer, found `&{integer}`
-   |
-help: consider removing the borrow
-   |
-LL -         asm!("{}", const &0);
-LL +         asm!("{}", const 0);
-   |
-
-error[E0308]: mismatched types
-  --> $DIR/type-check-1.rs:75:25
-   |
-LL | global_asm!("{}", const 0f32);
-   |                         ^^^^ expected integer, found `f32`
-
-error[E0308]: mismatched types
-  --> $DIR/type-check-1.rs:77:25
-   |
-LL | global_asm!("{}", const 0 as *mut u8);
-   |                         ^^^^^^^^^^^^ expected integer, found `*mut u8`
-   |
-   = note:     expected type `{integer}`
-           found raw pointer `*mut u8`
-
-error: aborting due to 17 previous errors
+error: aborting due to 8 previous errors
 
-Some errors have detailed explanations: E0277, E0308, E0435.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/asm/x86_64/type-check-2.rs b/tests/ui/asm/x86_64/type-check-2.rs
index 4b5d59fdbc785..ff811961462df 100644
--- a/tests/ui/asm/x86_64/type-check-2.rs
+++ b/tests/ui/asm/x86_64/type-check-2.rs
@@ -27,17 +27,6 @@ fn main() {
         asm!("{}", out(reg) v[0]);
         asm!("{}", inout(reg) v[0]);
 
-        // Sym operands must point to a function or static
-
-        const C: i32 = 0;
-        static S: i32 = 0;
-        asm!("{}", sym S);
-        asm!("{}", sym main);
-        asm!("{}", sym C);
-        //~^ ERROR invalid `sym` operand
-        asm!("{}", sym x);
-        //~^ ERROR invalid `sym` operand
-
         // Register operands must be Copy
 
         asm!("{}", in(xmm_reg) SimdNonCopy(0.0, 0.0, 0.0, 0.0));
@@ -79,12 +68,3 @@ fn main() {
         asm!("{}", in(reg) u);
     }
 }
-
-// Sym operands must point to a function or static
-
-const C: i32 = 0;
-static S: i32 = 0;
-global_asm!("{}", sym S);
-global_asm!("{}", sym main);
-global_asm!("{}", sym C);
-//~^ ERROR invalid `sym` operand
diff --git a/tests/ui/asm/x86_64/type-check-2.stderr b/tests/ui/asm/x86_64/type-check-2.stderr
index 6ae118b16e766..c72e695aefb83 100644
--- a/tests/ui/asm/x86_64/type-check-2.stderr
+++ b/tests/ui/asm/x86_64/type-check-2.stderr
@@ -1,37 +1,13 @@
-error: invalid `sym` operand
-  --> $DIR/type-check-2.rs:38:24
-   |
-LL |         asm!("{}", sym x);
-   |                        ^ is a local variable
-   |
-   = help: `sym` operands must refer to either a function or a static
-
-error: invalid `sym` operand
-  --> $DIR/type-check-2.rs:89:19
-   |
-LL | global_asm!("{}", sym C);
-   |                   ^^^^^ is an `i32`
-   |
-   = help: `sym` operands must refer to either a function or a static
-
-error: invalid `sym` operand
-  --> $DIR/type-check-2.rs:36:20
-   |
-LL |         asm!("{}", sym C);
-   |                    ^^^^^ is an `i32`
-   |
-   = help: `sym` operands must refer to either a function or a static
-
 error: arguments for inline assembly must be copyable
-  --> $DIR/type-check-2.rs:43:32
+  --> $DIR/type-check-2.rs:32:32
    |
 LL |         asm!("{}", in(xmm_reg) SimdNonCopy(0.0, 0.0, 0.0, 0.0));
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `SimdNonCopy` does not implement the Copy trait
 
-error: cannot use value of type `{closure@$DIR/type-check-2.rs:55:28: 55:36}` for inline assembly
-  --> $DIR/type-check-2.rs:55:28
+error: cannot use value of type `{closure@$DIR/type-check-2.rs:44:28: 44:36}` for inline assembly
+  --> $DIR/type-check-2.rs:44:28
    |
 LL |         asm!("{}", in(reg) |x: i32| x);
    |                            ^^^^^^^^^^
@@ -39,7 +15,7 @@ LL |         asm!("{}", in(reg) |x: i32| x);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `Vec` for inline assembly
-  --> $DIR/type-check-2.rs:57:28
+  --> $DIR/type-check-2.rs:46:28
    |
 LL |         asm!("{}", in(reg) vec![0]);
    |                            ^^^^^^^
@@ -48,7 +24,7 @@ LL |         asm!("{}", in(reg) vec![0]);
    = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: cannot use value of type `(i32, i32, i32)` for inline assembly
-  --> $DIR/type-check-2.rs:59:28
+  --> $DIR/type-check-2.rs:48:28
    |
 LL |         asm!("{}", in(reg) (1, 2, 3));
    |                            ^^^^^^^^^
@@ -56,7 +32,7 @@ LL |         asm!("{}", in(reg) (1, 2, 3));
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `[i32; 3]` for inline assembly
-  --> $DIR/type-check-2.rs:61:28
+  --> $DIR/type-check-2.rs:50:28
    |
 LL |         asm!("{}", in(reg) [1, 2, 3]);
    |                            ^^^^^^^^^
@@ -64,7 +40,7 @@ LL |         asm!("{}", in(reg) [1, 2, 3]);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `fn() {main}` for inline assembly
-  --> $DIR/type-check-2.rs:69:31
+  --> $DIR/type-check-2.rs:58:31
    |
 LL |         asm!("{}", inout(reg) f);
    |                               ^
@@ -72,7 +48,7 @@ LL |         asm!("{}", inout(reg) f);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `&mut i32` for inline assembly
-  --> $DIR/type-check-2.rs:72:31
+  --> $DIR/type-check-2.rs:61:31
    |
 LL |         asm!("{}", inout(reg) r);
    |                               ^
@@ -121,7 +97,7 @@ help: consider changing this to be mutable
 LL |         let mut v: Vec = vec![0, 1, 2];
    |             +++
 
-error: aborting due to 13 previous errors
+error: aborting due to 10 previous errors
 
 Some errors have detailed explanations: E0381, E0596.
 For more information about an error, try `rustc --explain E0381`.
diff --git a/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.rs b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.rs
new file mode 100644
index 0000000000000..f5f8fa51e37d9
--- /dev/null
+++ b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.rs
@@ -0,0 +1,15 @@
+trait Trait {
+    type Type;
+
+    fn one(&self, val:  impl Trait);
+    //~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied
+
+    fn two>(&self) -> T;
+    //~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied
+
+    fn three(&self) -> T where
+        T: Trait,;
+    //~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied
+}
+
+fn main() {}
diff --git a/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.stderr b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.stderr
new file mode 100644
index 0000000000000..06ab06003a1aa
--- /dev/null
+++ b/tests/ui/associated-type-bounds/name-same-as-generic-type-issue-128249.stderr
@@ -0,0 +1,51 @@
+error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied
+  --> $DIR/name-same-as-generic-type-issue-128249.rs:4:30
+   |
+LL |     fn one(&self, val:  impl Trait);
+   |                              ^^^^^ expected 1 generic argument
+   |
+note: trait defined here, with 1 generic parameter: `Type`
+  --> $DIR/name-same-as-generic-type-issue-128249.rs:1:7
+   |
+LL | trait Trait {
+   |       ^^^^^ ----
+help: add missing generic argument
+   |
+LL |     fn one(&self, val:  impl Trait);
+   |                                    +++++
+
+error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied
+  --> $DIR/name-same-as-generic-type-issue-128249.rs:7:15
+   |
+LL |     fn two>(&self) -> T;
+   |               ^^^^^ expected 1 generic argument
+   |
+note: trait defined here, with 1 generic parameter: `Type`
+  --> $DIR/name-same-as-generic-type-issue-128249.rs:1:7
+   |
+LL | trait Trait {
+   |       ^^^^^ ----
+help: add missing generic argument
+   |
+LL |     fn two>(&self) -> T;
+   |                     +++++
+
+error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied
+  --> $DIR/name-same-as-generic-type-issue-128249.rs:11:12
+   |
+LL |         T: Trait,;
+   |            ^^^^^ expected 1 generic argument
+   |
+note: trait defined here, with 1 generic parameter: `Type`
+  --> $DIR/name-same-as-generic-type-issue-128249.rs:1:7
+   |
+LL | trait Trait {
+   |       ^^^^^ ----
+help: add missing generic argument
+   |
+LL |         T: Trait,;
+   |                  +++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr
index 1164869bf1982..bd6022fcec655 100644
--- a/tests/ui/associated-types/associated-types-outlives.stderr
+++ b/tests/ui/associated-types/associated-types-outlives.stderr
@@ -18,7 +18,7 @@ LL | pub fn free_and_use Foo<'a>,
    |                     ^ consider constraining this type parameter with `Clone`
 ...
 LL |         's: loop { y = denormalise(&x); break }
-   |                                    -- you could clone this value
+   |                                     - you could clone this value
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/async-await/unreachable-lint-2.rs b/tests/ui/async-await/unreachable-lint-2.rs
new file mode 100644
index 0000000000000..137cb32481b9c
--- /dev/null
+++ b/tests/ui/async-await/unreachable-lint-2.rs
@@ -0,0 +1,15 @@
+//@ edition:2018
+
+#![deny(unreachable_code)]
+
+async fn foo() {
+    endless().await;
+    println!("this is unreachable!");
+    //~^ ERROR unreachable statement
+}
+
+async fn endless() -> ! {
+    loop {}
+}
+
+fn main() { }
diff --git a/tests/ui/async-await/unreachable-lint-2.stderr b/tests/ui/async-await/unreachable-lint-2.stderr
new file mode 100644
index 0000000000000..cbebc9951f32d
--- /dev/null
+++ b/tests/ui/async-await/unreachable-lint-2.stderr
@@ -0,0 +1,17 @@
+error: unreachable statement
+  --> $DIR/unreachable-lint-2.rs:7:5
+   |
+LL |     endless().await;
+   |               ----- any code following this expression is unreachable
+LL |     println!("this is unreachable!");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
+   |
+note: the lint level is defined here
+  --> $DIR/unreachable-lint-2.rs:3:9
+   |
+LL | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/attributes/check-builtin-attr-ice.rs b/tests/ui/attributes/check-builtin-attr-ice.rs
new file mode 100644
index 0000000000000..9ef5890601f6b
--- /dev/null
+++ b/tests/ui/attributes/check-builtin-attr-ice.rs
@@ -0,0 +1,58 @@
+//! Regression test for #128622.
+//!
+//! PR #128581 introduced an assertion that all builtin attributes are actually checked via
+//! `CheckAttrVisitor` and aren't accidentally usable on completely unrelated HIR nodes.
+//! Unfortunately, the check had correctness problems.
+//!
+//! The match on attribute path segments looked like
+//!
+//! ```rs,ignore
+//! [sym::should_panic] => /* check is implemented */
+//! match BUILTIN_ATTRIBUTE_MAP.get(name) {
+//!     // checked below
+//!     Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {}
+//!     Some(_) => {
+//!         if !name.as_str().starts_with("rustc_") {
+//!             span_bug!(
+//!                 attr.span,
+//!                 "builtin attribute {name:?} not handled by `CheckAttrVisitor`"
+//!             )
+//!         }
+//!     }
+//!     None => (),
+//! }
+//! ```
+//!
+//! However, it failed to account for edge cases such as an attribute whose:
+//!
+//! 1. path segments *starts* with a builtin attribute such as `should_panic`
+//! 2. which does not start with `rustc_`, and
+//! 3. is also an `AttributeType::Normal` attribute upon registration with the builtin attribute map
+//!
+//! These conditions when all satisfied cause the span bug to be issued for e.g.
+//! `#[should_panic::skip]` because the `[sym::should_panic]` arm is not matched (since it's
+//! `[sym::should_panic, sym::skip]`).
+//!
+//! This test checks that the span bug is not fired for such cases.
+//!
+//! issue: rust-lang/rust#128622
+
+// Notably, `should_panic` is a `AttributeType::Normal` attribute that is checked separately.
+
+struct Foo {
+    #[should_panic::skip]
+    //~^ ERROR failed to resolve
+    pub field: u8,
+
+    #[should_panic::a::b::c]
+    //~^ ERROR failed to resolve
+    pub field2: u8,
+}
+
+fn foo() {}
+
+fn main() {
+    #[deny::skip]
+    //~^ ERROR failed to resolve
+    foo();
+}
diff --git a/tests/ui/attributes/check-builtin-attr-ice.stderr b/tests/ui/attributes/check-builtin-attr-ice.stderr
new file mode 100644
index 0000000000000..5a27da565a857
--- /dev/null
+++ b/tests/ui/attributes/check-builtin-attr-ice.stderr
@@ -0,0 +1,21 @@
+error[E0433]: failed to resolve: use of undeclared crate or module `should_panic`
+  --> $DIR/check-builtin-attr-ice.rs:43:7
+   |
+LL |     #[should_panic::skip]
+   |       ^^^^^^^^^^^^ use of undeclared crate or module `should_panic`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `should_panic`
+  --> $DIR/check-builtin-attr-ice.rs:47:7
+   |
+LL |     #[should_panic::a::b::c]
+   |       ^^^^^^^^^^^^ use of undeclared crate or module `should_panic`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `deny`
+  --> $DIR/check-builtin-attr-ice.rs:55:7
+   |
+LL |     #[deny::skip]
+   |       ^^^^ use of undeclared crate or module `deny`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/tests/ui/attributes/check-cfg_attr-ice.rs b/tests/ui/attributes/check-cfg_attr-ice.rs
new file mode 100644
index 0000000000000..5bf8baab14140
--- /dev/null
+++ b/tests/ui/attributes/check-cfg_attr-ice.rs
@@ -0,0 +1,68 @@
+//! I missed a `cfg_attr` match in #128581, it should have had the same treatment as `cfg`. If
+//! an invalid attribute starting with `cfg_attr` is passed, then it would trigger an ICE because
+//! it was not considered "checked" (e.g. `#[cfg_attr::skip]` or `#[cfg_attr::no_such_thing]`).
+//!
+//! This test is not exhaustive, there's too many possible positions to check, instead it just does
+//! a basic smoke test in a few select positions to make sure we don't ICE for e.g.
+//! `#[cfg_attr::no_such_thing]`.
+//!
+//! issue: rust-lang/rust#128716
+#![crate_type = "lib"]
+
+#[cfg_attr::no_such_thing]
+//~^ ERROR failed to resolve
+mod we_are_no_strangers_to_love {}
+
+#[cfg_attr::no_such_thing]
+//~^ ERROR failed to resolve
+struct YouKnowTheRules {
+    #[cfg_attr::no_such_thing]
+    //~^ ERROR failed to resolve
+    and_so_do_i: u8,
+}
+
+#[cfg_attr::no_such_thing]
+//~^ ERROR failed to resolve
+fn a_full_commitment() {
+    #[cfg_attr::no_such_thing]
+    //~^ ERROR failed to resolve
+    let is_what_i_am_thinking_of = ();
+}
+
+#[cfg_attr::no_such_thing]
+//~^ ERROR failed to resolve
+union AnyOtherGuy {
+    owo: ()
+}
+struct This;
+
+#[cfg_attr(FALSE, doc = "you wouldn't get this")]
+impl From for This {
+    #[cfg_attr::no_such_thing]
+    //~^ ERROR failed to resolve
+    fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This {
+        //~^ ERROR failed to resolve
+        #[cfg_attr::no_such_thing]
+        //~^ ERROR attributes on expressions are experimental
+        //~| ERROR failed to resolve
+        unreachable!()
+    }
+}
+
+#[cfg_attr::no_such_thing]
+//~^ ERROR failed to resolve
+enum NeverGonna {
+    #[cfg_attr::no_such_thing]
+    //~^ ERROR failed to resolve
+    GiveYouUp(#[cfg_attr::no_such_thing] u8),
+    //~^ ERROR failed to resolve
+    LetYouDown {
+        #![cfg_attr::no_such_thing]
+        //~^ ERROR an inner attribute is not permitted in this context
+        never_gonna: (),
+        round_around: (),
+        #[cfg_attr::no_such_thing]
+        //~^ ERROR failed to resolve
+        and_desert_you: (),
+    },
+}
diff --git a/tests/ui/attributes/check-cfg_attr-ice.stderr b/tests/ui/attributes/check-cfg_attr-ice.stderr
new file mode 100644
index 0000000000000..dbdf32597f7b2
--- /dev/null
+++ b/tests/ui/attributes/check-cfg_attr-ice.stderr
@@ -0,0 +1,101 @@
+error: an inner attribute is not permitted in this context
+  --> $DIR/check-cfg_attr-ice.rs:60:9
+   |
+LL |         #![cfg_attr::no_such_thing]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
+   = note: outer attributes, like `#[test]`, annotate the item following them
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/check-cfg_attr-ice.rs:45:9
+   |
+LL |         #[cfg_attr::no_such_thing]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701  for more information
+   = help: add `#![feature(stmt_expr_attributes)]` 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[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:52:3
+   |
+LL | #[cfg_attr::no_such_thing]
+   |   ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:55:7
+   |
+LL |     #[cfg_attr::no_such_thing]
+   |       ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:57:17
+   |
+LL |     GiveYouUp(#[cfg_attr::no_such_thing] u8),
+   |                 ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:64:11
+   |
+LL |         #[cfg_attr::no_such_thing]
+   |           ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:41:7
+   |
+LL |     #[cfg_attr::no_such_thing]
+   |       ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:43:15
+   |
+LL |     fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This {
+   |               ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:45:11
+   |
+LL |         #[cfg_attr::no_such_thing]
+   |           ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:32:3
+   |
+LL | #[cfg_attr::no_such_thing]
+   |   ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:24:3
+   |
+LL | #[cfg_attr::no_such_thing]
+   |   ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:27:7
+   |
+LL |     #[cfg_attr::no_such_thing]
+   |       ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:16:3
+   |
+LL | #[cfg_attr::no_such_thing]
+   |   ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:19:7
+   |
+LL |     #[cfg_attr::no_such_thing]
+   |       ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr`
+  --> $DIR/check-cfg_attr-ice.rs:12:3
+   |
+LL | #[cfg_attr::no_such_thing]
+   |   ^^^^^^^^ use of undeclared crate or module `cfg_attr`
+
+error: aborting due to 15 previous errors
+
+Some errors have detailed explanations: E0433, E0658.
+For more information about an error, try `rustc --explain E0433`.
diff --git a/tests/ui/attributes/field-attributes-vis-unresolved.stderr b/tests/ui/attributes/field-attributes-vis-unresolved.stderr
index 819cd859ae909..f8610c08b0220 100644
--- a/tests/ui/attributes/field-attributes-vis-unresolved.stderr
+++ b/tests/ui/attributes/field-attributes-vis-unresolved.stderr
@@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistent`
 LL |     pub(in nonexistent) field: u8
    |            ^^^^^^^^^^^ you might be missing crate `nonexistent`
    |
-   = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate
+help: consider importing the `nonexistent` crate
+   |
+LL + extern crate nonexistent;
+   |
 
 error[E0433]: failed to resolve: you might be missing crate `nonexistent`
   --> $DIR/field-attributes-vis-unresolved.rs:22:12
@@ -12,7 +15,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistent`
 LL |     pub(in nonexistent) u8
    |            ^^^^^^^^^^^ you might be missing crate `nonexistent`
    |
-   = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate
+help: consider importing the `nonexistent` crate
+   |
+LL + extern crate nonexistent;
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/attributes/optimize.rs b/tests/ui/attributes/optimize.rs
new file mode 100644
index 0000000000000..b01806165c1f5
--- /dev/null
+++ b/tests/ui/attributes/optimize.rs
@@ -0,0 +1,28 @@
+#![feature(optimize_attribute)]
+#![feature(stmt_expr_attributes)]
+#![deny(unused_attributes)]
+#![allow(dead_code)]
+
+#[optimize(speed)] //~ ERROR attribute should be applied to function or closure
+struct F;
+
+fn invalid() {
+    #[optimize(speed)] //~ ERROR attribute should be applied to function or closure
+    {
+        1
+    };
+}
+
+#[optimize(speed)]
+fn valid() {}
+
+#[optimize(speed)]
+mod valid_module {}
+
+#[optimize(speed)]
+impl F {}
+
+fn main() {
+    let _ = #[optimize(speed)]
+    (|| 1);
+}
diff --git a/tests/ui/attributes/optimize.stderr b/tests/ui/attributes/optimize.stderr
new file mode 100644
index 0000000000000..3c445d73c2edb
--- /dev/null
+++ b/tests/ui/attributes/optimize.stderr
@@ -0,0 +1,20 @@
+error: attribute should be applied to function or closure
+  --> $DIR/optimize.rs:6:1
+   |
+LL | #[optimize(speed)]
+   | ^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/optimize.rs:3:9
+   |
+LL | #![deny(unused_attributes)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: attribute should be applied to function or closure
+  --> $DIR/optimize.rs:10:5
+   |
+LL |     #[optimize(speed)]
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
index 774ce86c0960c..b8edb4aab907b 100644
--- a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
@@ -1,6 +1,15 @@
 #![feature(unsafe_attributes)]
 
-#[derive(unsafe(Debug))] //~ ERROR: traits in `#[derive(...)]` don't accept `unsafe(...)`
+#[derive(unsafe(Debug))]
+//~^ ERROR: expected identifier, found keyword `unsafe`
+//~| ERROR: traits in `#[derive(...)]` don't accept arguments
+//~| ERROR: expected identifier, found keyword `unsafe`
+//~| ERROR: expected identifier, found keyword `unsafe`
+//~| ERROR: cannot find derive macro `r#unsafe` in this scope
+//~| ERROR: cannot find derive macro `r#unsafe` in this scope
 struct Foo;
 
+#[unsafe(derive(Debug))] //~ ERROR: is not an unsafe attribute
+struct Bar;
+
 fn main() {}
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
index fc0daf16790a8..c40a5512fd5cd 100644
--- a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
@@ -1,8 +1,65 @@
-error: traits in `#[derive(...)]` don't accept `unsafe(...)`
+error: expected identifier, found keyword `unsafe`
+  --> $DIR/derive-unsafe-attributes.rs:3:10
+   |
+LL | #[derive(unsafe(Debug))]
+   |          ^^^^^^ expected identifier, found keyword
+   |
+help: escape `unsafe` to use it as an identifier
+   |
+LL | #[derive(r#unsafe(Debug))]
+   |          ++
+
+error: traits in `#[derive(...)]` don't accept arguments
+  --> $DIR/derive-unsafe-attributes.rs:3:16
+   |
+LL | #[derive(unsafe(Debug))]
+   |                ^^^^^^^ help: remove the arguments
+
+error: `derive` is not an unsafe attribute
+  --> $DIR/derive-unsafe-attributes.rs:12:3
+   |
+LL | #[unsafe(derive(Debug))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: expected identifier, found keyword `unsafe`
+  --> $DIR/derive-unsafe-attributes.rs:3:10
+   |
+LL | #[derive(unsafe(Debug))]
+   |          ^^^^^^ expected identifier, found keyword
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: escape `unsafe` to use it as an identifier
+   |
+LL | #[derive(r#unsafe(Debug))]
+   |          ++
+
+error: expected identifier, found keyword `unsafe`
+  --> $DIR/derive-unsafe-attributes.rs:3:10
+   |
+LL | #[derive(unsafe(Debug))]
+   |          ^^^^^^ expected identifier, found keyword
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: escape `unsafe` to use it as an identifier
+   |
+LL | #[derive(r#unsafe(Debug))]
+   |          ++
+
+error: cannot find derive macro `r#unsafe` in this scope
   --> $DIR/derive-unsafe-attributes.rs:3:10
    |
 LL | #[derive(unsafe(Debug))]
    |          ^^^^^^
 
-error: aborting due to 1 previous error
+error: cannot find derive macro `r#unsafe` in this scope
+  --> $DIR/derive-unsafe-attributes.rs:3:10
+   |
+LL | #[derive(unsafe(Debug))]
+   |          ^^^^^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 7 previous errors
 
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
index ea82bac6df07b..950b2636993c1 100644
--- a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
@@ -13,7 +13,7 @@ error: `r#unsafe` is not an unsafe attribute
   --> $DIR/double-unsafe-attributes.rs:3:3
    |
 LL | #[unsafe(unsafe(no_mangle))]
-   |   ^^^^^^
+   |   ^^^^^^ this is not an unsafe attribute
    |
    = note: extraneous unsafe is not allowed in attributes
 
diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
new file mode 100644
index 0000000000000..0181add843bcd
--- /dev/null
+++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.rs
@@ -0,0 +1,31 @@
+//@ edition: 2024
+//@ compile-flags: -Zunstable-options
+#![feature(unsafe_attributes)]
+
+#[unsafe(cfg(any()))] //~ ERROR: is not an unsafe attribute
+fn a() {}
+
+#[unsafe(cfg_attr(any(), allow(dead_code)))] //~ ERROR: is not an unsafe attribute
+fn b() {}
+
+#[unsafe(test)] //~ ERROR: is not an unsafe attribute
+fn aa() {}
+
+#[unsafe(ignore = "test")] //~ ERROR: is not an unsafe attribute
+fn bb() {}
+
+#[unsafe(should_panic(expected = "test"))] //~ ERROR: is not an unsafe attribute
+fn cc() {}
+
+#[unsafe(macro_use)] //~ ERROR: is not an unsafe attribute
+mod inner {
+    #[unsafe(macro_export)] //~ ERROR: is not an unsafe attribute
+    macro_rules! m {
+        () => {};
+    }
+}
+
+#[unsafe(used)] //~ ERROR: is not an unsafe attribute
+static FOO: usize = 0;
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
new file mode 100644
index 0000000000000..f39074b613d40
--- /dev/null
+++ b/tests/ui/attributes/unsafe/extraneous-unsafe-attributes.stderr
@@ -0,0 +1,66 @@
+error: `cfg` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:5:3
+   |
+LL | #[unsafe(cfg(any()))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `cfg_attr` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:8:3
+   |
+LL | #[unsafe(cfg_attr(any(), allow(dead_code)))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `test` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:11:3
+   |
+LL | #[unsafe(test)]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `ignore` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:14:3
+   |
+LL | #[unsafe(ignore = "test")]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `should_panic` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:17:3
+   |
+LL | #[unsafe(should_panic(expected = "test"))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `macro_use` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:20:3
+   |
+LL | #[unsafe(macro_use)]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `macro_export` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:22:7
+   |
+LL |     #[unsafe(macro_export)]
+   |       ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `used` is not an unsafe attribute
+  --> $DIR/extraneous-unsafe-attributes.rs:28:3
+   |
+LL | #[unsafe(used)]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
new file mode 100644
index 0000000000000..f29a5b3252b0a
--- /dev/null
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs
@@ -0,0 +1,37 @@
+#![feature(unsafe_attributes)]
+
+#[unsafe(proc_macro)]
+//~^ ERROR: is not an unsafe attribute
+//~| ERROR attribute is only usable with crates of the `proc-macro` crate type
+pub fn a() {}
+
+
+#[unsafe(proc_macro_derive(Foo))]
+//~^ ERROR: is not an unsafe attribute
+//~| ERROR attribute is only usable with crates of the `proc-macro` crate type
+pub fn b() {}
+
+#[proc_macro_derive(unsafe(Foo))]
+//~^ ERROR attribute is only usable with crates of the `proc-macro` crate type
+//~| ERROR: expected identifier, found keyword `unsafe`
+pub fn c() {}
+
+#[unsafe(proc_macro_attribute)]
+//~^ ERROR: is not an unsafe attribute
+//~| ERROR attribute is only usable with crates of the `proc-macro` crate type
+pub fn d() {}
+
+#[unsafe(allow(dead_code))]
+//~^ ERROR: is not an unsafe attribute
+pub fn e() {}
+
+#[unsafe(allow(unsafe(dead_code)))]
+//~^ ERROR: is not an unsafe attribute
+//~| ERROR: malformed lint attribute input
+//~| ERROR: malformed lint attribute input
+//~| ERROR: expected identifier, found keyword `unsafe`
+//~| ERROR: malformed lint attribute input
+//~| ERROR: malformed lint attribute input
+pub fn f() {}
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
new file mode 100644
index 0000000000000..79d34d458bd6c
--- /dev/null
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
@@ -0,0 +1,119 @@
+error[E0452]: malformed lint attribute input
+  --> $DIR/proc-unsafe-attributes.rs:28:16
+   |
+LL | #[unsafe(allow(unsafe(dead_code)))]
+   |                ^^^^^^^^^^^^^^^^^ bad attribute argument
+
+error[E0452]: malformed lint attribute input
+  --> $DIR/proc-unsafe-attributes.rs:28:16
+   |
+LL | #[unsafe(allow(unsafe(dead_code)))]
+   |                ^^^^^^^^^^^^^^^^^ bad attribute argument
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: `proc_macro` is not an unsafe attribute
+  --> $DIR/proc-unsafe-attributes.rs:3:3
+   |
+LL | #[unsafe(proc_macro)]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `proc_macro_derive` is not an unsafe attribute
+  --> $DIR/proc-unsafe-attributes.rs:9:3
+   |
+LL | #[unsafe(proc_macro_derive(Foo))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: expected identifier, found keyword `unsafe`
+  --> $DIR/proc-unsafe-attributes.rs:14:21
+   |
+LL | #[proc_macro_derive(unsafe(Foo))]
+   |                     ^^^^^^ expected identifier, found keyword
+   |
+help: escape `unsafe` to use it as an identifier
+   |
+LL | #[proc_macro_derive(r#unsafe(Foo))]
+   |                     ++
+
+error: `proc_macro_attribute` is not an unsafe attribute
+  --> $DIR/proc-unsafe-attributes.rs:19:3
+   |
+LL | #[unsafe(proc_macro_attribute)]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `allow` is not an unsafe attribute
+  --> $DIR/proc-unsafe-attributes.rs:24:3
+   |
+LL | #[unsafe(allow(dead_code))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: `allow` is not an unsafe attribute
+  --> $DIR/proc-unsafe-attributes.rs:28:3
+   |
+LL | #[unsafe(allow(unsafe(dead_code)))]
+   |   ^^^^^^ this is not an unsafe attribute
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: expected identifier, found keyword `unsafe`
+  --> $DIR/proc-unsafe-attributes.rs:28:16
+   |
+LL | #[unsafe(allow(unsafe(dead_code)))]
+   |                ^^^^^^ expected identifier, found keyword
+   |
+help: escape `unsafe` to use it as an identifier
+   |
+LL | #[unsafe(allow(r#unsafe(dead_code)))]
+   |                ++
+
+error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type
+  --> $DIR/proc-unsafe-attributes.rs:3:1
+   |
+LL | #[unsafe(proc_macro)]
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
+  --> $DIR/proc-unsafe-attributes.rs:9:1
+   |
+LL | #[unsafe(proc_macro_derive(Foo))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
+  --> $DIR/proc-unsafe-attributes.rs:14:1
+   |
+LL | #[proc_macro_derive(unsafe(Foo))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type
+  --> $DIR/proc-unsafe-attributes.rs:19:1
+   |
+LL | #[unsafe(proc_macro_attribute)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0452]: malformed lint attribute input
+  --> $DIR/proc-unsafe-attributes.rs:28:16
+   |
+LL | #[unsafe(allow(unsafe(dead_code)))]
+   |                ^^^^^^^^^^^^^^^^^ bad attribute argument
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0452]: malformed lint attribute input
+  --> $DIR/proc-unsafe-attributes.rs:28:16
+   |
+LL | #[unsafe(allow(unsafe(dead_code)))]
+   |                ^^^^^^^^^^^^^^^^^ bad attribute argument
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 15 previous errors
+
+For more information about this error, try `rustc --explain E0452`.
diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs
index e7620a18048e4..33a412add500c 100644
--- a/tests/ui/attributes/unsafe/unsafe-attributes.rs
+++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs
@@ -4,4 +4,10 @@
 #[unsafe(no_mangle)]
 fn a() {}
 
+#[unsafe(export_name = "foo")]
+fn b() {}
+
+#[cfg_attr(any(), unsafe(no_mangle))]
+static VAR2: u32 = 1;
+
 fn main() {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
index 0602af34e4f64..584b0ea797d0a 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
@@ -2,7 +2,7 @@ error: `repr` is not an unsafe attribute
   --> $DIR/unsafe-safe-attribute.rs:3:3
    |
 LL | #[unsafe(repr(C))]
-   |   ^^^^^^
+   |   ^^^^^^ this is not an unsafe attribute
    |
    = note: extraneous unsafe is not allowed in attributes
 
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
index 584dacf4d8c91..26b5e4e37b931 100644
--- a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
@@ -2,7 +2,7 @@ error: `diagnostic::on_unimplemented` is not an unsafe attribute
   --> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
    |
 LL | #[unsafe(diagnostic::on_unimplemented(
-   |   ^^^^^^
+   |   ^^^^^^ this is not an unsafe attribute
    |
    = note: extraneous unsafe is not allowed in attributes
 
diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/augmented-assignments.rs
index 8b263e03593b8..440a4a7fd6500 100644
--- a/tests/ui/augmented-assignments.rs
+++ b/tests/ui/augmented-assignments.rs
@@ -17,7 +17,6 @@ fn main() {
     x;
     //~^ ERROR cannot move out of `x` because it is borrowed
     //~| move out of `x` occurs here
-    //~| HELP consider cloning
 
     let y = Int(2);
     //~^ HELP consider changing this to be mutable
diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/augmented-assignments.stderr
index 6b2900dd5d1bc..a4b75cbf6e8fc 100644
--- a/tests/ui/augmented-assignments.stderr
+++ b/tests/ui/augmented-assignments.stderr
@@ -8,14 +8,9 @@ LL |     x
 ...
 LL |     x;
    |     ^ move out of `x` occurs here
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |     x.clone();
-   |      ++++++++
 
 error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
-  --> $DIR/augmented-assignments.rs:25:5
+  --> $DIR/augmented-assignments.rs:24:5
    |
 LL |     y
    |     ^ cannot borrow as mutable
diff --git a/tests/ui/backtrace/synchronized-panic-handler.rs b/tests/ui/backtrace/synchronized-panic-handler.rs
index 00eb249da1dd5..29431ae3c458a 100644
--- a/tests/ui/backtrace/synchronized-panic-handler.rs
+++ b/tests/ui/backtrace/synchronized-panic-handler.rs
@@ -3,6 +3,7 @@
 //@ edition:2021
 //@ exec-env:RUST_BACKTRACE=0
 //@ needs-threads
+//@ needs-unwind
 use std::thread;
 const PANIC_MESSAGE: &str = "oops oh no woe is me";
 
diff --git a/tests/ui/backtrace/synchronized-panic-handler.run.stderr b/tests/ui/backtrace/synchronized-panic-handler.run.stderr
index b7c815a94c868..8a06d00ea5998 100644
--- a/tests/ui/backtrace/synchronized-panic-handler.run.stderr
+++ b/tests/ui/backtrace/synchronized-panic-handler.run.stderr
@@ -1,5 +1,5 @@
-thread '' panicked at $DIR/synchronized-panic-handler.rs:10:5:
+thread '' panicked at $DIR/synchronized-panic-handler.rs:11:5:
 oops oh no woe is me
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-thread '' panicked at $DIR/synchronized-panic-handler.rs:10:5:
+thread '' panicked at $DIR/synchronized-panic-handler.rs:11:5:
 oops oh no woe is me
diff --git a/tests/ui/binop/binop-move-semantics.stderr b/tests/ui/binop/binop-move-semantics.stderr
index 83c27590e9014..45c7f11040616 100644
--- a/tests/ui/binop/binop-move-semantics.stderr
+++ b/tests/ui/binop/binop-move-semantics.stderr
@@ -65,7 +65,7 @@ help: if `T` implemented `Clone`, you could clone the value
 LL | fn move_borrowed>(x: T, mut y: T) {
    |                  ^ consider constraining this type parameter with `Clone`
 LL |     let m = &x;
-   |             -- you could clone this value
+   |              - you could clone this value
 
 error[E0505]: cannot move out of `y` because it is borrowed
   --> $DIR/binop-move-semantics.rs:23:5
@@ -88,7 +88,7 @@ LL | fn move_borrowed>(x: T, mut y: T) {
    |                  ^ consider constraining this type parameter with `Clone`
 LL |     let m = &x;
 LL |     let n = &mut y;
-   |             ------ you could clone this value
+   |                  - you could clone this value
 
 error[E0507]: cannot move out of `*m` which is behind a mutable reference
   --> $DIR/binop-move-semantics.rs:30:5
diff --git a/tests/ui/borrowck/borrow-tuple-fields.stderr b/tests/ui/borrowck/borrow-tuple-fields.stderr
index 8ea7a9a498947..277c335cfccaa 100644
--- a/tests/ui/borrowck/borrow-tuple-fields.stderr
+++ b/tests/ui/borrowck/borrow-tuple-fields.stderr
@@ -13,9 +13,8 @@ LL |     r.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let r = &x.0;
-LL +     let r = x.0.clone();
-   |
+LL |     let r = &x.0.clone();
+   |                 ++++++++
 
 error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
   --> $DIR/borrow-tuple-fields.rs:18:13
@@ -51,9 +50,8 @@ LL |     r.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let r = &x.0;
-LL +     let r = x.0.clone();
-   |
+LL |     let r = &x.0.clone();
+   |                 ++++++++
 
 error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
   --> $DIR/borrow-tuple-fields.rs:33:13
diff --git a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr
index b96949fbb0e78..5e7eee0422ee2 100644
--- a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr
+++ b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr
@@ -14,7 +14,7 @@ LL |         a);
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -         &*a,
-LL +         a.clone(),
+LL +         &a.clone(),
    |
 
 error[E0505]: cannot move out of `a` because it is borrowed
@@ -32,7 +32,7 @@ LL |         a);
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -         &*a,
-LL +         a.clone(),
+LL +         &a.clone(),
    |
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/borrowck/borrowck-field-sensitivity.stderr b/tests/ui/borrowck/borrowck-field-sensitivity.stderr
index ea552ff7820e2..b30dd144a4d80 100644
--- a/tests/ui/borrowck/borrowck-field-sensitivity.stderr
+++ b/tests/ui/borrowck/borrowck-field-sensitivity.stderr
@@ -52,9 +52,8 @@ LL |     drop(**p);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let p = &x.b;
-LL +     let p = x.b.clone();
-   |
+LL |     let p = &x.b.clone();
+   |                 ++++++++
 
 error[E0505]: cannot move out of `x.b` because it is borrowed
   --> $DIR/borrowck-field-sensitivity.rs:41:14
@@ -70,9 +69,8 @@ LL |     drop(**p);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let p = &x.b;
-LL +     let p = x.b.clone();
-   |
+LL |     let p = &x.b.clone();
+   |                 ++++++++
 
 error[E0499]: cannot borrow `x.a` as mutable more than once at a time
   --> $DIR/borrowck-field-sensitivity.rs:48:13
diff --git a/tests/ui/borrowck/borrowck-issue-48962.stderr b/tests/ui/borrowck/borrowck-issue-48962.stderr
index 6e821a4c6b0b1..ee174f6736e1e 100644
--- a/tests/ui/borrowck/borrowck-issue-48962.stderr
+++ b/tests/ui/borrowck/borrowck-issue-48962.stderr
@@ -17,11 +17,6 @@ LL |     {src};
    |      --- value moved here
 LL |     src.0 = 66;
    |     ^^^^^^^^^^ value used here after move
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |     {src.clone()};
-   |         ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
index 370ae058f444d..d9af5cf7d12e8 100644
--- a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
+++ b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
@@ -16,9 +16,8 @@ LL |     w.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let w = &v;
-LL +     let w = v.clone();
-   |
+LL |     let w = &v.clone();
+   |               ++++++++
 
 error[E0505]: cannot move out of `v` because it is borrowed
   --> $DIR/borrowck-loan-blocks-move-cc.rs:24:19
@@ -38,9 +37,8 @@ LL |     w.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let w = &v;
-LL +     let w = v.clone();
-   |
+LL |     let w = &v.clone();
+   |               ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr
index 8a8005dbb8337..1698035f20ffb 100644
--- a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr
+++ b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr
@@ -12,9 +12,8 @@ LL |     w.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let w = &v;
-LL +     let w = v.clone();
-   |
+LL |     let w = &v.clone();
+   |               ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
index a23a203d99978..b78c374c45620 100644
--- a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
+++ b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
@@ -13,9 +13,8 @@ LL |     b.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let b = &a;
-LL +     let b = a.clone();
-   |
+LL |     let b = &a.clone();
+   |               ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr
index acf426906c3b2..4b9351b64a038 100644
--- a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr
+++ b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr
@@ -14,7 +14,7 @@ LL |     p.use_ref();
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -     let p: &isize = &*t0; // Freezes `*t0`
-LL +     let p: &isize = t0.clone(); // Freezes `*t0`
+LL +     let p: &isize = &t0.clone(); // Freezes `*t0`
    |
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr
index cc6c3bdeb1022..b5dc01f180b2f 100644
--- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr
+++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr
@@ -17,7 +17,7 @@ LL | struct S {
    | ^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |   let pb = &a;
-   |            -- you could clone this value
+   |             - you could clone this value
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-multiple-captures.stderr b/tests/ui/borrowck/borrowck-multiple-captures.stderr
index fdac4c27cee3b..01b648ea64717 100644
--- a/tests/ui/borrowck/borrowck-multiple-captures.stderr
+++ b/tests/ui/borrowck/borrowck-multiple-captures.stderr
@@ -17,9 +17,8 @@ LL |     borrow(&*p1);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let p1 = &x1;
-LL +     let p1 = x1.clone();
-   |
+LL |     let p1 = &x1.clone();
+   |                 ++++++++
 
 error[E0505]: cannot move out of `x2` because it is borrowed
   --> $DIR/borrowck-multiple-captures.rs:12:19
@@ -39,9 +38,8 @@ LL |     borrow(&*p2);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let p2 = &x2;
-LL +     let p2 = x2.clone();
-   |
+LL |     let p2 = &x2.clone();
+   |                 ++++++++
 
 error[E0382]: use of moved value: `x1`
   --> $DIR/borrowck-multiple-captures.rs:27:19
@@ -108,9 +106,8 @@ LL |     borrow(&*p);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let p = &x;
-LL +     let p = x.clone();
-   |
+LL |     let p = &x.clone();
+   |               ++++++++
 
 error[E0382]: use of moved value: `x`
   --> $DIR/borrowck-multiple-captures.rs:52:14
diff --git a/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr b/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr
index 7f8cc74a7157a..3366853b8e268 100644
--- a/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr
+++ b/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr
@@ -11,6 +11,11 @@ LL |     println!("{}", f[s]);
 ...
 LL |     use_mut(rs);
    |             -- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let rs = &mut s.clone();
+   |                    ++++++++
 
 error[E0505]: cannot move out of `s` because it is borrowed
   --> $DIR/borrowck-overloaded-index-move-index.rs:53:7
@@ -25,6 +30,11 @@ LL |     f[s] = 10;
 ...
 LL |     use_mut(rs);
    |             -- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let rs = &mut s.clone();
+   |                    ++++++++
 
 error[E0382]: use of moved value: `s`
   --> $DIR/borrowck-overloaded-index-move-index.rs:53:7
diff --git a/tests/ui/borrowck/borrowck-unary-move.stderr b/tests/ui/borrowck/borrowck-unary-move.stderr
index 598ecb537780f..0aec6d426309f 100644
--- a/tests/ui/borrowck/borrowck-unary-move.stderr
+++ b/tests/ui/borrowck/borrowck-unary-move.stderr
@@ -13,7 +13,7 @@ LL |     *y
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -     let y = &*x;
-LL +     let y = x.clone();
+LL +     let y = &x.clone();
    |
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr
index 19f040556f8ae..d5d21296a3f96 100644
--- a/tests/ui/borrowck/clone-on-ref.stderr
+++ b/tests/ui/borrowck/clone-on-ref.stderr
@@ -38,7 +38,7 @@ help: if `T` implemented `Clone`, you could clone the value
 LL | fn bar(x: T) {
    |        ^ consider constraining this type parameter with `Clone`
 LL |     let a = &x;
-   |             -- you could clone this value
+   |              - you could clone this value
 help: consider further restricting this bound
    |
 LL | fn bar(x: T) {
@@ -66,7 +66,7 @@ LL | struct A;
    | ^^^^^^^^ consider implementing `Clone` for this type
 LL | fn qux(x: A) {
 LL |     let a = &x;
-   |             -- you could clone this value
+   |              - you could clone this value
 help: consider annotating `A` with `#[derive(Clone)]`
    |
 LL + #[derive(Clone)]
diff --git a/tests/ui/borrowck/issue-101119.stderr b/tests/ui/borrowck/issue-101119.stderr
index a894fa63ace28..7fec81b59b3a8 100644
--- a/tests/ui/borrowck/issue-101119.stderr
+++ b/tests/ui/borrowck/issue-101119.stderr
@@ -18,14 +18,6 @@ LL | fn fill_segment(_: &mut State) {}
    |    ------------    ^^^^^^^^^^ this parameter takes ownership of the value
    |    |
    |    in this function
-note: if `State` implemented `Clone`, you could clone the value
-  --> $DIR/issue-101119.rs:1:1
-   |
-LL | struct State;
-   | ^^^^^^^^^^^^ consider implementing `Clone` for this type
-...
-LL |             fill_segment(state);
-   |                          ----- you could clone this value
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/btreemap/btreemap_dropck.stderr b/tests/ui/btreemap/btreemap_dropck.stderr
index 873f8cf9a0147..e8f14552af26d 100644
--- a/tests/ui/btreemap/btreemap_dropck.stderr
+++ b/tests/ui/btreemap/btreemap_dropck.stderr
@@ -12,9 +12,8 @@ LL | }
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let _map = BTreeMap::from_iter([((), PrintOnDrop(&s))]);
-LL +     let _map = BTreeMap::from_iter([((), PrintOnDrop(s.clone()))]);
-   |
+LL |     let _map = BTreeMap::from_iter([((), PrintOnDrop(&s.clone()))]);
+   |                                                        ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/cast/dyn-tails-need-normalization.rs b/tests/ui/cast/dyn-tails-need-normalization.rs
new file mode 100644
index 0000000000000..719e0e892430d
--- /dev/null
+++ b/tests/ui/cast/dyn-tails-need-normalization.rs
@@ -0,0 +1,21 @@
+//@ check-pass
+
+trait Trait {
+    type Associated;
+}
+
+impl Trait for i32 {
+    type Associated = i64;
+}
+
+trait Generic {}
+
+type TraitObject = dyn Generic<::Associated>;
+
+struct Wrap(TraitObject);
+
+fn cast(x: *mut TraitObject) {
+    x as *mut Wrap;
+}
+
+fn main() {}
diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr
index 57cbe173c7896..520cffc4b0268 100644
--- a/tests/ui/check-cfg/mix.stderr
+++ b/tests/ui/check-cfg/mix.stderr
@@ -251,7 +251,7 @@ warning: unexpected `cfg` condition value: `zebra`
 LL |     cfg!(target_feature = "zebra");
    |          ^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `amx-bf16`, `amx-complex`, `amx-fp16`, `amx-int8`, `amx-tile`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, and `avx512vpopcntdq` and 199 more
+   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `amx-bf16`, `amx-complex`, `amx-fp16`, `amx-int8`, `amx-tile`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, and `avx512vpopcntdq` and 201 more
    = note: see  for more information about checking conditional configuration
 
 warning: 27 warnings emitted
diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr
index 00abb5f5e5ca0..d780e04e729a2 100644
--- a/tests/ui/check-cfg/well-known-values.stderr
+++ b/tests/ui/check-cfg/well-known-values.stderr
@@ -165,7 +165,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
 LL |     target_feature = "_UNEXPECTED_VALUE",
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `amx-bf16`, `amx-complex`, `amx-fp16`, `amx-int8`, `amx-tile`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `avxifma`, `avxneconvert`, `avxvnni`, `avxvnniint16`, `avxvnniint8`, `backchain`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vector`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xop`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, and `zkt`
+   = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `amx-bf16`, `amx-complex`, `amx-fp16`, `amx-int8`, `amx-tile`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `avxifma`, `avxneconvert`, `avxvnni`, `avxvnniint16`, `avxvnniint8`, `backchain`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sha512`, `sign-ext`, `simd128`, `sm3`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vector`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xop`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, and `zkt`
    = note: see  for more information about checking conditional configuration
 
 warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
diff --git a/tests/ui/coherence/re-rebalance-coherence.rs b/tests/ui/coherence/re-rebalance-coherence.rs
index 5383a634617f4..9c176d5b1b12b 100644
--- a/tests/ui/coherence/re-rebalance-coherence.rs
+++ b/tests/ui/coherence/re-rebalance-coherence.rs
@@ -4,7 +4,6 @@
 extern crate re_rebalance_coherence_lib as lib;
 use lib::*;
 
-#[allow(dead_code)]
 struct Oracle;
 impl Backend for Oracle {}
 impl<'a, T:'a, Tab> QueryFragment for BatchInsert<'a, T, Tab> {}
diff --git a/tests/ui/const-generics/cross_crate_complex.rs b/tests/ui/const-generics/cross_crate_complex.rs
index b44d889f5e99e..d13b69aa0cfb4 100644
--- a/tests/ui/const-generics/cross_crate_complex.rs
+++ b/tests/ui/const-generics/cross_crate_complex.rs
@@ -11,7 +11,6 @@ async fn foo() {
     async_in_foo(async_out_foo::<4>().await).await;
 }
 
-#[allow(dead_code)]
 struct Faz;
 
 impl Foo for Faz {}
diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
index 4bf2fa761eae8..c23187598bceb 100644
--- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
+++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
@@ -2,7 +2,6 @@
 
 //@ run-pass
 
-#[allow(dead_code)]
 #[repr(C)]
 pub struct Loaf {
     head: [T; N],
diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
index 50a6102c605a9..5d2198f50ad92 100644
--- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
+++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
@@ -16,8 +16,7 @@ impl BlockCipher for BarCipher {
     const BLOCK_SIZE: usize = 32;
 }
 
-#[allow(dead_code)]
-pub struct Block(C);
+pub struct Block(#[allow(dead_code)] C);
 
 pub fn test()
 where
diff --git a/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs
index 4ba696f4ae080..9f20cf0857947 100644
--- a/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs
+++ b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs
@@ -9,9 +9,10 @@ fn bug(&self)
 where
     for [(); COT::BYTES]:,
     //~^ ERROR only lifetime parameters can be used in this context
-    //~^^ ERROR defaults for generic parameters are not allowed in `for<...>` binders
-    //~^^^ ERROR defaults for generic parameters are not allowed in `for<...>` binders
-    //~^^^^ ERROR failed to resolve: use of undeclared type `COT`
+    //~| ERROR defaults for generic parameters are not allowed in `for<...>` binders
+    //~| ERROR defaults for generic parameters are not allowed in `for<...>` binders
+    //~| ERROR failed to resolve: use of undeclared type `COT`
+    //~| ERROR  the name `N` is already used for a generic parameter in this item's generic parameters
 {
 }
 
diff --git a/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr
index ee0ec38ab0631..6037e685e44d7 100644
--- a/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr
@@ -6,6 +6,15 @@ LL | fn bug(&self)
    |
    = note: associated functions are those in `impl` or `trait` definitions
 
+error[E0403]: the name `N` is already used for a generic parameter in this item's generic parameters
+  --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:10:15
+   |
+LL | fn bug(&self)
+   |              - first use of `N`
+...
+LL |     for [(); COT::BYTES]:,
+   |               ^ already used
+
 error[E0412]: cannot find type `Nat` in this scope
   --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:6:17
    |
@@ -40,7 +49,7 @@ error[E0433]: failed to resolve: use of undeclared type `COT`
 LL |     for [(); COT::BYTES]:,
    |                                           ^^^ use of undeclared type `COT`
 
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0412, E0433, E0658.
-For more information about an error, try `rustc --explain E0412`.
+Some errors have detailed explanations: E0403, E0412, E0433, E0658.
+For more information about an error, try `rustc --explain E0403`.
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr b/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr
index 09b618fb3f050..1c97eaddfe1bb 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr
@@ -4,10 +4,6 @@ error[E0282]: type annotations needed
 LL |     Combination::<0>.and::<_>().and::<_>();
    |                      ^^^ cannot infer type of the type parameter `M` declared on the method `and`
    |
-help: consider specifying the generic argument
-   |
-LL |     Combination::<0>.and::<_>().and::<_>();
-   |                         ~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr b/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr
index 8d59235e2f448..01529599d37d0 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr
@@ -1,4 +1,4 @@
-error: internal compiler error: compiler/rustc_const_eval/src/interpret/step.rs:LL:CC: SizeOf MIR operator called for unsized type dyn Debug
+error: internal compiler error: compiler/rustc_const_eval/src/interpret/operator.rs:LL:CC: unsized type for `NullaryOp::SizeOf`
   --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
 
 Box
diff --git a/tests/ui/const-generics/issues/issue-86535-2.rs b/tests/ui/const-generics/issues/issue-86535-2.rs
index ab68c6b78df34..8d064f3eeb1b7 100644
--- a/tests/ui/const-generics/issues/issue-86535-2.rs
+++ b/tests/ui/const-generics/issues/issue-86535-2.rs
@@ -9,7 +9,6 @@ pub trait Foo {
         [(); Self::ASSOC_C]:;
 }
 
-#[allow(dead_code)]
 struct Bar;
 impl Foo for Bar {
     const ASSOC_C: usize = 3;
diff --git a/tests/ui/const-generics/issues/issue-86535.rs b/tests/ui/const-generics/issues/issue-86535.rs
index 9aaf7ddc9e86e..62454f4a388a0 100644
--- a/tests/ui/const-generics/issues/issue-86535.rs
+++ b/tests/ui/const-generics/issues/issue-86535.rs
@@ -2,7 +2,6 @@
 #![feature(adt_const_params, unsized_const_params, generic_const_exprs)]
 #![allow(incomplete_features, unused_variables)]
 
-#[allow(dead_code)]
 struct F;
 impl X for F<{ S }> {
     const W: usize = 3;
diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
index 35c41ae461520..419d605d0c875 100644
--- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
+++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
@@ -6,7 +6,6 @@
 
 use std::mem::MaybeUninit;
 
-#[allow(dead_code)]
 #[repr(transparent)]
 pub struct MaybeUninitWrapper(MaybeUninit<[u64; N]>);
 
diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr
index 034e8bd185264..fad078ad2b2bd 100644
--- a/tests/ui/const-ptr/forbidden_slices.stderr
+++ b/tests/ui/const-ptr/forbidden_slices.stderr
@@ -118,7 +118,7 @@ LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
 error[E0080]: could not evaluate static initializer
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC10 and there are only 4 bytes starting at that pointer
+   = note: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC10 which is only 4 bytes from the end of the allocation
    |
 note: inside `std::ptr::const_ptr::::add`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -177,7 +177,7 @@ LL | pub static R7: &[u16] = unsafe {
 error[E0080]: could not evaluate static initializer
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC11+0x1 and there are only 7 bytes starting at that pointer
+   = note: out-of-bounds pointer arithmetic: expected a pointer to 8 bytes of memory, but got ALLOC11+0x1 which is only 7 bytes from the end of the allocation
    |
 note: inside `std::ptr::const_ptr::::add`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr
index 8a923779a5bb4..7f83dee640969 100644
--- a/tests/ui/consts/const-compare-bytes-ub.stderr
+++ b/tests/ui/consts/const-compare-bytes-ub.stderr
@@ -20,13 +20,13 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:22:9
    |
 LL |         compare_bytes([1, 2, 3].as_ptr(), [1, 2, 3, 4].as_ptr(), 4)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0 and there are only 3 bytes starting at that pointer
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC0 which is only 3 bytes from the end of the allocation
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:26:9
    |
 LL |         compare_bytes([1, 2, 3, 4].as_ptr(), [1, 2, 3].as_ptr(), 4)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC1 and there are only 3 bytes starting at that pointer
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 4 bytes of memory, but got ALLOC1 which is only 3 bytes from the end of the allocation
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:30:9
diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr
index 5fce25701bdd1..aeb46725c0642 100644
--- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr
+++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr
@@ -35,7 +35,7 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/raw-pointer-ub.rs:41:16
    |
 LL |     let _val = *ptr;
-   |                ^^^^ memory access failed: expected a pointer to 8 bytes of memory, but got ALLOC0 and there are only 4 bytes starting at that pointer
+   |                ^^^^ memory access failed: expected a pointer to 8 bytes of memory, but got ALLOC0 which is only 4 bytes from the end of the allocation
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr
index fe3060dda17df..0e4926eb49edf 100644
--- a/tests/ui/consts/const-eval/ub-nonnull.stderr
+++ b/tests/ui/consts/const-eval/ub-nonnull.stderr
@@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/ub-nonnull.rs:20:29
    |
 LL |     let out_of_bounds_ptr = &ptr[255];
-   |                             ^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 255 bytes of memory, but got ALLOC1 and there are only 1 bytes starting at that pointer
+   |                             ^^^^^^^^^ out-of-bounds pointer arithmetic: expected a pointer to 255 bytes of memory, but got ALLOC1 which is only 1 byte from the end of the allocation
 
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-nonnull.rs:24:1
diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs
index 66bb056ceb049..7efc5dd3e28ba 100644
--- a/tests/ui/consts/offset_from_ub.rs
+++ b/tests/ui/consts/offset_from_ub.rs
@@ -1,4 +1,4 @@
-//@ normalize-stderr-test: "to \d+ bytes of memory" -> "to $$BYTES bytes of memory"
+//@ normalize-stderr-test: "\d+ bytes" -> "$$BYTES bytes"
 #![feature(const_ptr_sub_ptr)]
 #![feature(core_intrinsics)]
 
@@ -55,7 +55,7 @@ const OUT_OF_BOUNDS_2: isize = {
     let end_ptr = (start_ptr).wrapping_add(length);
     // Second ptr is out of bounds
     unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed
-    //~| expected a pointer to 10 bytes of memory
+    //~| expected a pointer to the end of 10 bytes of memory
 };
 
 pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr
index f2f27735630fe..ac4597ff0119a 100644
--- a/tests/ui/consts/offset_from_ub.stderr
+++ b/tests/ui/consts/offset_from_ub.stderr
@@ -27,19 +27,19 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:39:14
    |
 LL |     unsafe { ptr_offset_from(ptr2, ptr1) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: expected a pointer to $BYTES bytes of memory, but got 0x8[noalloc] which is a dangling pointer (it has no provenance)
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got 0x8[noalloc] which is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:48:14
    |
 LL |     unsafe { ptr_offset_from(end_ptr, start_ptr) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: expected a pointer to $BYTES bytes of memory, but got ALLOC0 and there are only 4 bytes starting at that pointer
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got ALLOC0 which is only $BYTES bytes from the end of the allocation
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:57:14
    |
 LL |     unsafe { ptr_offset_from(start_ptr, end_ptr) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: expected a pointer to $BYTES bytes of memory, but got ALLOC1 and there are only 4 bytes starting at that pointer
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC1+0xa which does not have enough space to the beginning of the allocation
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:66:14
@@ -80,7 +80,7 @@ LL |     unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds `offset_from`: expected a pointer to $BYTES bytes of memory, but got a null pointer
+   = note: out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got a null pointer
    |
 note: inside `std::ptr::const_ptr::::offset_from`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs
index b239b91e11cf6..5026d9a271322 100644
--- a/tests/ui/consts/offset_ub.rs
+++ b/tests/ui/consts/offset_ub.rs
@@ -2,7 +2,7 @@ use std::ptr;
 
 //@ normalize-stderr-test: "0xf+" -> "0xf..f"
 //@ normalize-stderr-test: "0x7f+" -> "0x7f..f"
-//@ normalize-stderr-test: "to \d+ bytes of memory" -> "to $$BYTES bytes of memory"
+//@ normalize-stderr-test: "\d+ bytes" -> "$$BYTES bytes"
 
 
 pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; //~NOTE
diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr
index b42d9482f8a01..779cb9654f4d2 100644
--- a/tests/ui/consts/offset_ub.stderr
+++ b/tests/ui/consts/offset_ub.stderr
@@ -1,7 +1,7 @@
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: overflowing in-bounds pointer arithmetic
+   = note: out-of-bounds pointer arithmetic: expected a pointer to the end of 1 byte of memory, but got ALLOC0 which is at the beginning of the allocation
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -14,7 +14,7 @@ LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1)
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC0 and there are only 1 bytes starting at that pointer
+   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC1 which is only 1 byte from the end of the allocation
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -27,7 +27,7 @@ LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) };
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC1 and there are only 100 bytes starting at that pointer
+   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC2 which is only $BYTES bytes from the end of the allocation
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -40,7 +40,7 @@ LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101)
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: overflowing in-bounds pointer arithmetic
+   = note: overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -53,7 +53,7 @@ LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: overflowing in-bounds pointer arithmetic
+   = note: overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -66,7 +66,7 @@ LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize:
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: overflowing in-bounds pointer arithmetic
+   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance)
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -79,7 +79,7 @@ LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *cons
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: overflowing in-bounds pointer arithmetic
+   = note: out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got 0x1[noalloc] which is a dangling pointer (it has no provenance)
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -92,7 +92,7 @@ LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).of
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got ALLOC2-0x4 which points to before the beginning of the allocation
+   = note: out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC3-0x2 which points to before the beginning of the allocation
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -105,7 +105,7 @@ LL | pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC3 which is at or beyond the end of the allocation of size 0 bytes
+   = note: out-of-bounds pointer arithmetic: expected a pointer to 1 byte of memory, but got ALLOC4 which is at or beyond the end of the allocation of size $BYTES bytes
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -131,7 +131,7 @@ LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: expected a pointer to $BYTES bytes of memory, but got 0x7f..f[noalloc] which is a dangling pointer (it has no provenance)
+   = note: out-of-bounds pointer arithmetic: expected a pointer to the end of $BYTES bytes of memory, but got 0xf..f[noalloc] which is a dangling pointer (it has no provenance)
    |
 note: inside `std::ptr::const_ptr::::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
diff --git a/tests/ui/coroutine/invalid_attr_usage.rs b/tests/ui/coroutine/invalid_attr_usage.rs
new file mode 100644
index 0000000000000..995a3aa3100fc
--- /dev/null
+++ b/tests/ui/coroutine/invalid_attr_usage.rs
@@ -0,0 +1,11 @@
+//! The `coroutine` attribute is only allowed on closures.
+
+#![feature(coroutines)]
+
+#[coroutine]
+//~^ ERROR: attribute should be applied to closures
+struct Foo;
+
+#[coroutine]
+//~^ ERROR: attribute should be applied to closures
+fn main() {}
diff --git a/tests/ui/coroutine/invalid_attr_usage.stderr b/tests/ui/coroutine/invalid_attr_usage.stderr
new file mode 100644
index 0000000000000..316a0117e5d41
--- /dev/null
+++ b/tests/ui/coroutine/invalid_attr_usage.stderr
@@ -0,0 +1,14 @@
+error: attribute should be applied to closures
+  --> $DIR/invalid_attr_usage.rs:5:1
+   |
+LL | #[coroutine]
+   | ^^^^^^^^^^^^
+
+error: attribute should be applied to closures
+  --> $DIR/invalid_attr_usage.rs:9:1
+   |
+LL | #[coroutine]
+   | ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/derives/deriving-with-repr-packed.rs b/tests/ui/derives/deriving-with-repr-packed.rs
index 58be451972017..d17b52842cefd 100644
--- a/tests/ui/derives/deriving-with-repr-packed.rs
+++ b/tests/ui/derives/deriving-with-repr-packed.rs
@@ -22,25 +22,22 @@ struct Y(usize);
 struct X(Y);
 //~^ ERROR cannot move out of `self` which is behind a shared reference
 
-// This is currently allowed, but will be phased out at some point. From
-// `zerovec` within icu4x-0.9.0.
 #[derive(Debug)]
 #[repr(packed)]
 struct FlexZeroSlice {
     width: u8,
     data: [u8],
-    //~^ WARNING byte slice in a packed struct that derives a built-in trait
-    //~^^ this was previously accepted
+    //~^ ERROR cannot move
+    //~| ERROR cannot move
 }
 
-// Again, currently allowed, but will be phased out.
 #[derive(Debug)]
 #[repr(packed)]
 struct WithStr {
     width: u8,
     data: str,
-    //~^ WARNING string slice in a packed struct that derives a built-in trait
-    //~^^ this was previously accepted
+    //~^ ERROR cannot move
+    //~| ERROR cannot move
 }
 
 fn main() {}
diff --git a/tests/ui/derives/deriving-with-repr-packed.stderr b/tests/ui/derives/deriving-with-repr-packed.stderr
index a8523d25cab9b..321ffb27eeb11 100644
--- a/tests/ui/derives/deriving-with-repr-packed.stderr
+++ b/tests/ui/derives/deriving-with-repr-packed.stderr
@@ -1,32 +1,3 @@
-warning: byte slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-with-repr-packed.rs:31:5
-   |
-LL | #[derive(Debug)]
-   |          ----- in this derive macro expansion
-...
-LL |     data: [u8],
-   |     ^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default
-   = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-warning: string slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-with-repr-packed.rs:41:5
-   |
-LL | #[derive(Debug)]
-   |          ----- in this derive macro expansion
-...
-LL |     data: str,
-   |     ^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
-
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed.rs:22:10
    |
@@ -47,38 +18,43 @@ LL | struct X(Y);
    = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 1 previous error; 2 warnings emitted
+error[E0161]: cannot move a value of type `[u8]`
+  --> $DIR/deriving-with-repr-packed.rs:29:5
+   |
+LL |     data: [u8],
+   |     ^^^^^^^^^^ the size of `[u8]` cannot be statically determined
 
-For more information about this error, try `rustc --explain E0507`.
-Future incompatibility report: Future breakage diagnostic:
-warning: byte slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-with-repr-packed.rs:31:5
+error[E0507]: cannot move out of `self.data` which is behind a shared reference
+  --> $DIR/deriving-with-repr-packed.rs:29:5
    |
 LL | #[derive(Debug)]
    |          ----- in this derive macro expansion
 ...
 LL |     data: [u8],
-   |     ^^^^^^^^^^
+   |     ^^^^^^^^^^ move occurs because `self.data` has type `[u8]`, which does not implement the `Copy` trait
+   |
+   = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
+   = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0161]: cannot move a value of type `str`
+  --> $DIR/deriving-with-repr-packed.rs:38:5
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default
-   = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |     data: str,
+   |     ^^^^^^^^^ the size of `str` cannot be statically determined
 
-Future breakage diagnostic:
-warning: string slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-with-repr-packed.rs:41:5
+error[E0507]: cannot move out of `self.data` which is behind a shared reference
+  --> $DIR/deriving-with-repr-packed.rs:38:5
    |
 LL | #[derive(Debug)]
    |          ----- in this derive macro expansion
 ...
 LL |     data: str,
-   |     ^^^^^^^^^
+   |     ^^^^^^^^^ move occurs because `self.data` has type `str`, which does not implement the `Copy` trait
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default
-   = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
+   = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 5 previous errors
 
+Some errors have detailed explanations: E0161, E0507.
+For more information about an error, try `rustc --explain E0161`.
diff --git a/tests/ui/deriving/deriving-all-codegen.rs b/tests/ui/deriving/deriving-all-codegen.rs
index 6fa4f74f2a508..eab2b4f1f5335 100644
--- a/tests/ui/deriving/deriving-all-codegen.rs
+++ b/tests/ui/deriving/deriving-all-codegen.rs
@@ -73,16 +73,6 @@ impl Copy for PackedManualCopy {}
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
 struct Unsized([u32]);
 
-// A packed struct with an unsized `[u8]` field. This is currently allowed, but
-// causes a warning and will be phased out at some point.
-#[derive(Debug, Hash)]
-#[repr(packed)]
-struct PackedUnsizedU8([u8]);
-//~^ WARNING byte slice in a packed struct that derives a built-in trait
-//~^^ WARNING byte slice in a packed struct that derives a built-in trait
-//~^^^ this was previously accepted
-//~^^^^ this was previously accepted
-
 trait Trait {
     type A;
 }
diff --git a/tests/ui/deriving/deriving-all-codegen.stderr b/tests/ui/deriving/deriving-all-codegen.stderr
deleted file mode 100644
index 503f0cae73b69..0000000000000
--- a/tests/ui/deriving/deriving-all-codegen.stderr
+++ /dev/null
@@ -1,63 +0,0 @@
-warning: byte slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-all-codegen.rs:80:24
-   |
-LL | #[derive(Debug, Hash)]
-   |          ----- in this derive macro expansion
-LL | #[repr(packed)]
-LL | struct PackedUnsizedU8([u8]);
-   |                        ^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default
-   = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-warning: byte slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-all-codegen.rs:80:24
-   |
-LL | #[derive(Debug, Hash)]
-   |                 ---- in this derive macro expansion
-LL | #[repr(packed)]
-LL | struct PackedUnsizedU8([u8]);
-   |                        ^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: this warning originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-warning: 2 warnings emitted
-
-Future incompatibility report: Future breakage diagnostic:
-warning: byte slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-all-codegen.rs:80:24
-   |
-LL | #[derive(Debug, Hash)]
-   |          ----- in this derive macro expansion
-LL | #[repr(packed)]
-LL | struct PackedUnsizedU8([u8]);
-   |                        ^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default
-   = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-Future breakage diagnostic:
-warning: byte slice in a packed struct that derives a built-in trait
-  --> $DIR/deriving-all-codegen.rs:80:24
-   |
-LL | #[derive(Debug, Hash)]
-   |                 ---- in this derive macro expansion
-LL | #[repr(packed)]
-LL | struct PackedUnsizedU8([u8]);
-   |                        ^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #107457 
-   = help: consider implementing the trait by hand, or remove the `packed` attribute
-   = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default
-   = note: this warning originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
-
diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout
index 6b69b57c51619..6503c87099040 100644
--- a/tests/ui/deriving/deriving-all-codegen.stdout
+++ b/tests/ui/deriving/deriving-all-codegen.stdout
@@ -516,26 +516,6 @@ impl ::core::cmp::Ord for Unsized {
     }
 }
 
-// A packed struct with an unsized `[u8]` field. This is currently allowed, but
-// causes a warning and will be phased out at some point.
-#[repr(packed)]
-struct PackedUnsizedU8([u8]);
-#[automatically_derived]
-impl ::core::fmt::Debug for PackedUnsizedU8 {
-    #[inline]
-    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
-            "PackedUnsizedU8", &&self.0)
-    }
-}
-#[automatically_derived]
-impl ::core::hash::Hash for PackedUnsizedU8 {
-    #[inline]
-    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        ::core::hash::Hash::hash(&self.0, state)
-    }
-}
-
 trait Trait {
     type A;
 }
diff --git a/tests/ui/deriving/deriving-smart-pointer-expanded.rs b/tests/ui/deriving/deriving-smart-pointer-expanded.rs
new file mode 100644
index 0000000000000..b78258c25290e
--- /dev/null
+++ b/tests/ui/deriving/deriving-smart-pointer-expanded.rs
@@ -0,0 +1,22 @@
+//@ check-pass
+//@ compile-flags: -Zunpretty=expanded
+#![feature(derive_smart_pointer)]
+use std::marker::SmartPointer;
+
+pub trait MyTrait {}
+
+#[derive(SmartPointer)]
+#[repr(transparent)]
+struct MyPointer<'a, #[pointee] T: ?Sized> {
+    ptr: &'a T,
+}
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct MyPointer2<'a, Y, Z: MyTrait, #[pointee] T: ?Sized + MyTrait, X: MyTrait = ()>
+where
+    Y: MyTrait,
+{
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
diff --git a/tests/ui/deriving/deriving-smart-pointer-expanded.stdout b/tests/ui/deriving/deriving-smart-pointer-expanded.stdout
new file mode 100644
index 0000000000000..3c7e719818042
--- /dev/null
+++ b/tests/ui/deriving/deriving-smart-pointer-expanded.stdout
@@ -0,0 +1,44 @@
+#![feature(prelude_import)]
+#![no_std]
+//@ check-pass
+//@ compile-flags: -Zunpretty=expanded
+#![feature(derive_smart_pointer)]
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+use std::marker::SmartPointer;
+
+pub trait MyTrait {}
+
+#[repr(transparent)]
+struct MyPointer<'a, #[pointee] T: ?Sized> {
+    ptr: &'a T,
+}
+#[automatically_derived]
+impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
+    ::core::ops::DispatchFromDyn> for MyPointer<'a, T> {
+}
+#[automatically_derived]
+impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
+    ::core::ops::CoerceUnsized> for MyPointer<'a, T> {
+}
+
+#[repr(transparent)]
+pub struct MyPointer2<'a, Y, Z: MyTrait, #[pointee] T: ?Sized + MyTrait,
+    X: MyTrait = ()> where Y: MyTrait {
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+#[automatically_derived]
+impl<'a, Y, Z: MyTrait + MyTrait<__S>, T: ?Sized + MyTrait +
+    ::core::marker::Unsize<__S>, __S: ?Sized + MyTrait<__S>, X: MyTrait +
+    MyTrait<__S>> ::core::ops::DispatchFromDyn>
+    for MyPointer2<'a, Y, Z, T, X> where Y: MyTrait, Y: MyTrait<__S> {
+}
+#[automatically_derived]
+impl<'a, Y, Z: MyTrait + MyTrait<__S>, T: ?Sized + MyTrait +
+    ::core::marker::Unsize<__S>, __S: ?Sized + MyTrait<__S>, X: MyTrait +
+    MyTrait<__S>> ::core::ops::CoerceUnsized> for
+    MyPointer2<'a, Y, Z, T, X> where Y: MyTrait, Y: MyTrait<__S> {
+}
diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.rs b/tests/ui/deriving/deriving-smart-pointer-neg.rs
index bfb4e86b39601..04f52a154fe42 100644
--- a/tests/ui/deriving/deriving-smart-pointer-neg.rs
+++ b/tests/ui/deriving/deriving-smart-pointer-neg.rs
@@ -1,5 +1,6 @@
 #![feature(derive_smart_pointer, arbitrary_self_types)]
 
+extern crate core;
 use std::marker::SmartPointer;
 
 #[derive(SmartPointer)]
@@ -35,6 +36,13 @@ struct NotTransparent<'a, #[pointee] T: ?Sized> {
     ptr: &'a T,
 }
 
+#[derive(SmartPointer)]
+#[repr(transparent)]
+struct NoMaybeSized<'a, #[pointee] T> {
+    //~^ ERROR: `derive(SmartPointer)` requires T to be marked `?Sized`
+    ptr: &'a T,
+}
+
 // However, reordering attributes should work nevertheless.
 #[repr(transparent)]
 #[derive(SmartPointer)]
@@ -42,4 +50,26 @@ struct ThisIsAPossibleSmartPointer<'a, #[pointee] T: ?Sized> {
     ptr: &'a T,
 }
 
+// Also, these paths to Sized should work
+#[derive(SmartPointer)]
+#[repr(transparent)]
+struct StdSized<'a, #[pointee] T: ?std::marker::Sized> {
+    ptr: &'a T,
+}
+#[derive(SmartPointer)]
+#[repr(transparent)]
+struct CoreSized<'a, #[pointee] T: ?core::marker::Sized> {
+    ptr: &'a T,
+}
+#[derive(SmartPointer)]
+#[repr(transparent)]
+struct GlobalStdSized<'a, #[pointee] T: ?::std::marker::Sized> {
+    ptr: &'a T,
+}
+#[derive(SmartPointer)]
+#[repr(transparent)]
+struct GlobalCoreSized<'a, #[pointee] T: ?::core::marker::Sized> {
+    ptr: &'a T,
+}
+
 fn main() {}
diff --git a/tests/ui/deriving/deriving-smart-pointer-neg.stderr b/tests/ui/deriving/deriving-smart-pointer-neg.stderr
index d994a6ee376b0..8b0f91d41fb88 100644
--- a/tests/ui/deriving/deriving-smart-pointer-neg.stderr
+++ b/tests/ui/deriving/deriving-smart-pointer-neg.stderr
@@ -1,5 +1,5 @@
 error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
-  --> $DIR/deriving-smart-pointer-neg.rs:5:10
+  --> $DIR/deriving-smart-pointer-neg.rs:6:10
    |
 LL | #[derive(SmartPointer)]
    |          ^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL | #[derive(SmartPointer)]
    = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
-  --> $DIR/deriving-smart-pointer-neg.rs:11:10
+  --> $DIR/deriving-smart-pointer-neg.rs:12:10
    |
 LL | #[derive(SmartPointer)]
    |          ^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL | #[derive(SmartPointer)]
    = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `SmartPointer` can only be derived on `struct`s with at least one field
-  --> $DIR/deriving-smart-pointer-neg.rs:18:10
+  --> $DIR/deriving-smart-pointer-neg.rs:19:10
    |
 LL | #[derive(SmartPointer)]
    |          ^^^^^^^^^^^^
@@ -23,7 +23,7 @@ LL | #[derive(SmartPointer)]
    = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `SmartPointer` can only be derived on `struct`s with at least one field
-  --> $DIR/deriving-smart-pointer-neg.rs:25:10
+  --> $DIR/deriving-smart-pointer-neg.rs:26:10
    |
 LL | #[derive(SmartPointer)]
    |          ^^^^^^^^^^^^
@@ -31,15 +31,21 @@ LL | #[derive(SmartPointer)]
    = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
-  --> $DIR/deriving-smart-pointer-neg.rs:32:10
+  --> $DIR/deriving-smart-pointer-neg.rs:33:10
    |
 LL | #[derive(SmartPointer)]
    |          ^^^^^^^^^^^^
    |
    = note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+error: `derive(SmartPointer)` requires T to be marked `?Sized`
+  --> $DIR/deriving-smart-pointer-neg.rs:41:36
+   |
+LL | struct NoMaybeSized<'a, #[pointee] T> {
+   |                                    ^
+
 error[E0392]: lifetime parameter `'a` is never used
-  --> $DIR/deriving-smart-pointer-neg.rs:21:16
+  --> $DIR/deriving-smart-pointer-neg.rs:22:16
    |
 LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    |                ^^ unused lifetime parameter
@@ -47,7 +53,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: type parameter `T` is never used
-  --> $DIR/deriving-smart-pointer-neg.rs:21:31
+  --> $DIR/deriving-smart-pointer-neg.rs:22:31
    |
 LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    |                               ^ unused type parameter
@@ -55,7 +61,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: lifetime parameter `'a` is never used
-  --> $DIR/deriving-smart-pointer-neg.rs:28:20
+  --> $DIR/deriving-smart-pointer-neg.rs:29:20
    |
 LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
    |                    ^^ unused lifetime parameter
@@ -63,13 +69,13 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
    = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: type parameter `T` is never used
-  --> $DIR/deriving-smart-pointer-neg.rs:28:35
+  --> $DIR/deriving-smart-pointer-neg.rs:29:35
    |
 LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
    |                                   ^ unused type parameter
    |
    = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
 For more information about this error, try `rustc --explain E0392`.
diff --git a/tests/ui/deriving/smart-pointer-bounds-issue-127647.rs b/tests/ui/deriving/smart-pointer-bounds-issue-127647.rs
new file mode 100644
index 0000000000000..4cae1b32896fb
--- /dev/null
+++ b/tests/ui/deriving/smart-pointer-bounds-issue-127647.rs
@@ -0,0 +1,78 @@
+//@ check-pass
+
+#![feature(derive_smart_pointer)]
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr<'a, #[pointee] T: OnDrop + ?Sized, X> {
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+
+pub trait OnDrop {
+    fn on_drop(&mut self);
+}
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr2<'a, #[pointee] T: ?Sized, X>
+where
+    T: OnDrop,
+{
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+
+pub trait MyTrait {}
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr3<'a, #[pointee] T: ?Sized, X>
+where
+    T: MyTrait,
+{
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr4<'a, #[pointee] T: MyTrait + ?Sized, X> {
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr5<'a, #[pointee] T: ?Sized, X>
+where
+    Ptr5Companion: MyTrait,
+    Ptr5Companion2: MyTrait,
+{
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+
+pub struct Ptr5Companion(core::marker::PhantomData);
+pub struct Ptr5Companion2;
+
+#[derive(core::marker::SmartPointer)]
+#[repr(transparent)]
+pub struct Ptr6<'a, #[pointee] T: ?Sized, X: MyTrait = (), const PARAM: usize = 0> {
+    data: &'a mut T,
+    x: core::marker::PhantomData,
+}
+
+// a reduced example from https://lore.kernel.org/all/20240402-linked-list-v1-1-b1c59ba7ae3b@google.com/
+#[repr(transparent)]
+#[derive(core::marker::SmartPointer)]
+pub struct ListArc<#[pointee] T, const ID: u64 = 0>
+where
+    T: ListArcSafe + ?Sized,
+{
+    arc: *const T,
+}
+
+pub trait ListArcSafe {}
+
+fn main() {}
diff --git a/tests/ui/dropck/drop-with-active-borrows-1.stderr b/tests/ui/dropck/drop-with-active-borrows-1.stderr
index 7d1633267f0e2..229514c6feef0 100644
--- a/tests/ui/dropck/drop-with-active-borrows-1.stderr
+++ b/tests/ui/dropck/drop-with-active-borrows-1.stderr
@@ -9,11 +9,6 @@ LL |     drop(a);
    |          ^ move out of `a` occurs here
 LL |     for s in &b {
    |              -- borrow later used here
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |     let b: Vec<&str> = a.clone().lines().collect();
-   |                         ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/dropck/dropck-empty-array.rs b/tests/ui/dropck/dropck-empty-array.rs
new file mode 100644
index 0000000000000..f3eca6aed8d5e
--- /dev/null
+++ b/tests/ui/dropck/dropck-empty-array.rs
@@ -0,0 +1,23 @@
+//@ run-pass
+
+#[allow(dead_code)]
+struct Struct<'s>(&'s str);
+
+impl<'s> Drop for Struct<'s> {
+    fn drop(&mut self) {}
+}
+
+fn to_array_zero(_: T) -> [T; 0] {
+    []
+}
+
+pub fn array_zero_in_tuple() {
+    let mut x = ([], String::new());
+    {
+        let s = String::from("temporary");
+        let p = Struct(&s);
+        x.0 = to_array_zero(p);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/error-codes/E0432.stderr b/tests/ui/error-codes/E0432.stderr
index a0b17e35c94a8..36fefc95897d6 100644
--- a/tests/ui/error-codes/E0432.stderr
+++ b/tests/ui/error-codes/E0432.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `something`
 LL | use something::Foo;
    |     ^^^^^^^^^ you might be missing crate `something`
    |
-   = help: consider adding `extern crate something` to use the `something` crate
+help: consider importing the `something` crate
+   |
+LL + extern crate something;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr
index 343bca9a72e24..582669593336a 100644
--- a/tests/ui/error-codes/E0504.stderr
+++ b/tests/ui/error-codes/E0504.stderr
@@ -21,7 +21,7 @@ LL | struct FancyNum {
    | ^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |     let fancy_ref = &fancy_num;
-   |                     ---------- you could clone this value
+   |                      --------- you could clone this value
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr
index 266df9ea32a71..3f2913e9fe3c6 100644
--- a/tests/ui/error-codes/E0505.stderr
+++ b/tests/ui/error-codes/E0505.stderr
@@ -18,7 +18,7 @@ LL | struct Value {}
    | ^^^^^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |         let _ref_to_val: &Value = &x;
-   |                                   -- you could clone this value
+   |                                    - you could clone this value
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/extern/extern-types-field-offset.run.stderr b/tests/ui/extern/extern-types-field-offset.run.stderr
index 1b04b860db5a6..f14073989800b 100644
--- a/tests/ui/extern/extern-types-field-offset.run.stderr
+++ b/tests/ui/extern/extern-types-field-offset.run.stderr
@@ -1,4 +1,4 @@
-thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
+thread 'main' panicked at core/src/panicking.rs:$LINE:$COL:
 attempted to compute the size or alignment of extern type `Opaque`
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 thread caused non-unwinding panic. aborting.
diff --git a/tests/ui/extern/extern-types-size_of_val.align.run.stderr b/tests/ui/extern/extern-types-size_of_val.align.run.stderr
index 20c4d8785e846..faad1aa13faf6 100644
--- a/tests/ui/extern/extern-types-size_of_val.align.run.stderr
+++ b/tests/ui/extern/extern-types-size_of_val.align.run.stderr
@@ -1,4 +1,4 @@
-thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
+thread 'main' panicked at core/src/panicking.rs:$LINE:$COL:
 attempted to compute the size or alignment of extern type `A`
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 thread caused non-unwinding panic. aborting.
diff --git a/tests/ui/extern/extern-types-size_of_val.size.run.stderr b/tests/ui/extern/extern-types-size_of_val.size.run.stderr
index 20c4d8785e846..faad1aa13faf6 100644
--- a/tests/ui/extern/extern-types-size_of_val.size.run.stderr
+++ b/tests/ui/extern/extern-types-size_of_val.size.run.stderr
@@ -1,4 +1,4 @@
-thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
+thread 'main' panicked at core/src/panicking.rs:$LINE:$COL:
 attempted to compute the size or alignment of extern type `A`
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 thread caused non-unwinding panic. aborting.
diff --git a/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr b/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr
deleted file mode 100644
index 308de2692930d..0000000000000
--- a/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr
+++ /dev/null
@@ -1,21 +0,0 @@
-error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
-  --> $DIR/feature-gate-default_type_parameter_fallback.rs:3:8
-   |
-LL | fn avg(_: T) {}
-   |        ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #36887 
-   = note: `#[deny(invalid_type_param_default)]` on by default
-
-error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
-  --> $DIR/feature-gate-default_type_parameter_fallback.rs:8:6
-   |
-LL | impl S {}
-   |      ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #36887 
-
-error: aborting due to 2 previous errors
-
diff --git a/tests/ui/feature-gates/feature-gate-sha512_sm_x86.rs b/tests/ui/feature-gates/feature-gate-sha512_sm_x86.rs
new file mode 100644
index 0000000000000..176a40ecf5370
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-sha512_sm_x86.rs
@@ -0,0 +1,6 @@
+//@ only-x86_64
+#[target_feature(enable = "sha512")]
+//~^ ERROR: currently unstable
+unsafe fn foo() {}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-sha512_sm_x86.stderr b/tests/ui/feature-gates/feature-gate-sha512_sm_x86.stderr
new file mode 100644
index 0000000000000..da9eea095a332
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-sha512_sm_x86.stderr
@@ -0,0 +1,13 @@
+error[E0658]: the target feature `sha512` is currently unstable
+  --> $DIR/feature-gate-sha512_sm_x86.rs:2:18
+   |
+LL | #[target_feature(enable = "sha512")]
+   |                  ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #126624  for more information
+   = help: add `#![feature(sha512_sm_x86)]` 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-unsafe-extern-blocks.rs b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs
deleted file mode 100644
index 3ea62e875b8e3..0000000000000
--- a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-unsafe extern "C" {
-    //~^ ERROR extern block cannot be declared unsafe
-}
-
-// We can't gate `unsafe extern` blocks themselves since they were previously
-// allowed, but we should gate the `safe` soft keyword.
-#[cfg(any())]
-unsafe extern "C" {
-    safe fn foo();
-    //~^ ERROR `unsafe extern {}` blocks and `safe` keyword are experimental
-}
-
-fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr
deleted file mode 100644
index 5653494630899..0000000000000
--- a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error: extern block cannot be declared unsafe
-  --> $DIR/feature-gate-unsafe-extern-blocks.rs:1:1
-   |
-LL | unsafe extern "C" {
-   | ^^^^^^
-   |
-   = note: see issue #123743  for more information
-   = help: add `#![feature(unsafe_extern_blocks)]` 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]: `unsafe extern {}` blocks and `safe` keyword are experimental
-  --> $DIR/feature-gate-unsafe-extern-blocks.rs:9:5
-   |
-LL |     safe fn foo();
-   |     ^^^^
-   |
-   = note: see issue #123743  for more information
-   = help: add `#![feature(unsafe_extern_blocks)]` 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 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/version_check.rs b/tests/ui/feature-gates/version_check.rs
new file mode 100644
index 0000000000000..e212dc74fd123
--- /dev/null
+++ b/tests/ui/feature-gates/version_check.rs
@@ -0,0 +1,17 @@
+//@ run-pass
+//@ only-linux
+//@ only-x86
+// FIXME: this should be more like //@ needs-subprocesses
+use std::process::Command;
+
+fn main() {
+    let signalled_version = "Ceci n'est pas une rustc";
+    let version = Command::new(std::env::var_os("RUSTC").unwrap())
+        .env("RUSTC_OVERRIDE_VERSION_STRING", signalled_version)
+        .arg("--version")
+        .output()
+        .unwrap()
+        .stdout;
+    let version = std::str::from_utf8(&version).unwrap().strip_prefix("rustc ").unwrap().trim_end();
+    assert_eq!(version, signalled_version);
+}
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
index b8ec2e3b7e7b9..4e64ed6f482e0 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
@@ -13,9 +13,8 @@ LL |     println!("{}", y);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let y = f(&x, ());
-LL +     let y = f(x.clone(), ());
-   |
+LL |     let y = f(&x.clone(), ());
+   |                 ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.rs b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.rs
index a7489f6fbaff4..38be3250c7d66 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.rs
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.rs
@@ -5,6 +5,7 @@ trait Trait<'a>: 'a {
 // if the `T: 'a` bound gets implied we would probably get ub here again
 impl<'a, T> Trait<'a> for T {
     //~^ ERROR the parameter type `T` may not live long enough
+    //~| ERROR the parameter type `T` may not live long enough
     type Type = ();
 }
 
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
index 382ab8636a294..b2aa1abbbdb6e 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
@@ -16,8 +16,21 @@ help: consider adding an explicit lifetime bound
 LL | impl<'a, T: 'a> Trait<'a> for T {
    |           ++++
 
+error[E0309]: the parameter type `T` may not live long enough
+  --> $DIR/implied-bounds-unnorm-associated-type-5.rs:6:27
+   |
+LL | impl<'a, T> Trait<'a> for T {
+   |      --                   ^ ...so that the type `T` will meet its required lifetime bounds
+   |      |
+   |      the parameter type `T` must be valid for the lifetime `'a` as defined here...
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL | impl<'a, T: 'a> Trait<'a> for T {
+   |           ++++
+
 error[E0505]: cannot move out of `x` because it is borrowed
-  --> $DIR/implied-bounds-unnorm-associated-type-5.rs:21:10
+  --> $DIR/implied-bounds-unnorm-associated-type-5.rs:22:10
    |
 LL |     let x = String::from("Hello World!");
    |         - binding `x` declared here
@@ -30,11 +43,10 @@ LL |     println!("{}", y);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let y = f(&x, ());
-LL +     let y = f(x.clone(), ());
-   |
+LL |     let y = f(&x.clone(), ());
+   |                 ++++++++
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 Some errors have detailed explanations: E0309, E0505.
 For more information about an error, try `rustc --explain E0309`.
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr
index ce97d8527e857..2a7431305fed4 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr
@@ -13,9 +13,8 @@ LL |     println!("{}", y);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let y = f(&x, ());
-LL +     let y = f(x.clone(), ());
-   |
+LL |     let y = f(&x.clone(), ());
+   |                 ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/generic-associated-types/missing-bounds.fixed b/tests/ui/generic-associated-types/missing-bounds.fixed
index ff69016d8626d..703d3c1e0fb17 100644
--- a/tests/ui/generic-associated-types/missing-bounds.fixed
+++ b/tests/ui/generic-associated-types/missing-bounds.fixed
@@ -2,7 +2,6 @@
 
 use std::ops::Add;
 
-#[allow(dead_code)]
 struct A(B);
 
 impl Add for A where B: Add {
@@ -13,7 +12,6 @@ impl Add for A where B: Add {
     }
 }
 
-#[allow(dead_code)]
 struct C(B);
 
 impl> Add for C {
@@ -24,7 +22,6 @@ impl> Add for C {
     }
 }
 
-#[allow(dead_code)]
 struct D(B);
 
 impl> Add for D {
@@ -35,7 +32,6 @@ impl> Add for D {
     }
 }
 
-#[allow(dead_code)]
 struct E(B);
 
 impl> Add for E where B: Add {
diff --git a/tests/ui/generic-associated-types/missing-bounds.rs b/tests/ui/generic-associated-types/missing-bounds.rs
index 1f83356c2fa6b..f40b422887311 100644
--- a/tests/ui/generic-associated-types/missing-bounds.rs
+++ b/tests/ui/generic-associated-types/missing-bounds.rs
@@ -2,7 +2,6 @@
 
 use std::ops::Add;
 
-#[allow(dead_code)]
 struct A(B);
 
 impl Add for A where B: Add {
@@ -13,7 +12,6 @@ impl Add for A where B: Add {
     }
 }
 
-#[allow(dead_code)]
 struct C(B);
 
 impl Add for C {
@@ -24,7 +22,6 @@ impl Add for C {
     }
 }
 
-#[allow(dead_code)]
 struct D(B);
 
 impl Add for D {
@@ -35,7 +32,6 @@ impl Add for D {
     }
 }
 
-#[allow(dead_code)]
 struct E(B);
 
 impl Add for E where ::Output = B {
diff --git a/tests/ui/generic-associated-types/missing-bounds.stderr b/tests/ui/generic-associated-types/missing-bounds.stderr
index 0f0dc24c06c0f..1d7d80d1b0768 100644
--- a/tests/ui/generic-associated-types/missing-bounds.stderr
+++ b/tests/ui/generic-associated-types/missing-bounds.stderr
@@ -1,5 +1,5 @@
 error: equality constraints are not yet supported in `where` clauses
-  --> $DIR/missing-bounds.rs:41:33
+  --> $DIR/missing-bounds.rs:37:33
    |
 LL | impl Add for E where ::Output = B {
    |                                 ^^^^^^^^^^^^^^^^^^^^^^ not supported
@@ -11,7 +11,7 @@ LL | impl Add for E where B: Add {
    |                                 ~~~~~~~~~~~~~~~~~~
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:12:11
+  --> $DIR/missing-bounds.rs:11:11
    |
 LL | impl Add for A where B: Add {
    |      - expected this type parameter
@@ -24,14 +24,14 @@ LL |         A(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `::Output`
 help: the type constructed contains `::Output` due to the type of the argument passed
-  --> $DIR/missing-bounds.rs:12:9
+  --> $DIR/missing-bounds.rs:11:9
    |
 LL |         A(self.0 + rhs.0)
    |         ^^--------------^
    |           |
    |           this argument influences the type of `A`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:6:8
+  --> $DIR/missing-bounds.rs:5:8
    |
 LL | struct A(B);
    |        ^
@@ -41,7 +41,7 @@ LL | impl Add for A where B: Add {
    |                                  ++++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:23:14
+  --> $DIR/missing-bounds.rs:21:14
    |
 LL | impl Add for C {
    |      - expected this type parameter
@@ -54,7 +54,7 @@ LL |         Self(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:17:8
+  --> $DIR/missing-bounds.rs:15:8
    |
 LL | struct C(B);
    |        ^
@@ -64,7 +64,7 @@ LL | impl> Add for C {
    |            ++++++++++++
 
 error[E0369]: cannot add `B` to `B`
-  --> $DIR/missing-bounds.rs:34:21
+  --> $DIR/missing-bounds.rs:31:21
    |
 LL |         Self(self.0 + rhs.0)
    |              ------ ^ ----- B
@@ -77,7 +77,7 @@ LL | impl> Add for D {
    |       +++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
-  --> $DIR/missing-bounds.rs:46:14
+  --> $DIR/missing-bounds.rs:42:14
    |
 LL | impl Add for E where ::Output = B {
    |      - expected this type parameter
@@ -90,7 +90,7 @@ LL |         Self(self.0 + rhs.0)
    = note: expected type parameter `B`
              found associated type `::Output`
 note: tuple struct defined here
-  --> $DIR/missing-bounds.rs:39:8
+  --> $DIR/missing-bounds.rs:35:8
    |
 LL | struct E(B);
    |        ^
diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr
index 5c1778bc485f9..bfed4cd665030 100644
--- a/tests/ui/hygiene/panic-location.run.stderr
+++ b/tests/ui/hygiene/panic-location.run.stderr
@@ -1,3 +1,3 @@
-thread 'main' panicked at library/alloc/src/raw_vec.rs:LL:CC:
+thread 'main' panicked at alloc/src/raw_vec.rs:LL:CC:
 capacity overflow
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
index 3c4499f017337..886fc1d005802 100644
--- a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
+++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
@@ -1,8 +1,6 @@
 //@ run-rustfix
 
-#[allow(dead_code)]
 struct S(T);
-#[allow(dead_code)]
 struct S2;
 
 impl Default for S {
diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs
index ac0783295242e..f3271993867cb 100644
--- a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs
+++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs
@@ -1,8 +1,6 @@
 //@ run-rustfix
 
-#[allow(dead_code)]
 struct S(T);
-#[allow(dead_code)]
 struct S2;
 
 impl impl Default for S {
diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
index 91c7da5a04fb4..5aafc8b64d4ff 100644
--- a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
+++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
@@ -1,23 +1,23 @@
 error: unexpected `impl` keyword
-  --> $DIR/extra-impl-in-trait-impl.rs:8:18
+  --> $DIR/extra-impl-in-trait-impl.rs:6:18
    |
 LL | impl impl Default for S {
    |                  ^^^^^ help: remove the extra `impl`
    |
 note: this is parsed as an `impl Trait` type, but a trait is expected at this position
-  --> $DIR/extra-impl-in-trait-impl.rs:8:18
+  --> $DIR/extra-impl-in-trait-impl.rs:6:18
    |
 LL | impl impl Default for S {
    |                  ^^^^^^^^^^^^
 
 error: unexpected `impl` keyword
-  --> $DIR/extra-impl-in-trait-impl.rs:14:6
+  --> $DIR/extra-impl-in-trait-impl.rs:12:6
    |
 LL | impl impl Default for S2 {
    |      ^^^^^ help: remove the extra `impl`
    |
 note: this is parsed as an `impl Trait` type, but a trait is expected at this position
-  --> $DIR/extra-impl-in-trait-impl.rs:14:6
+  --> $DIR/extra-impl-in-trait-impl.rs:12:6
    |
 LL | impl impl Default for S2 {
    |      ^^^^^^^^^^^^
diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr
index f0d259d01de94..1fb69db98c162 100644
--- a/tests/ui/impl-trait/where-allowed.stderr
+++ b/tests/ui/impl-trait/where-allowed.stderr
@@ -433,3 +433,25 @@ error: aborting due to 50 previous errors
 
 Some errors have detailed explanations: E0053, E0118, E0283, E0562, E0599, E0658, E0666.
 For more information about an error, try `rustc --explain E0053`.
+Future incompatibility report: Future breakage diagnostic:
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/where-allowed.rs:239:7
+   |
+LL | impl  T {}
+   |       ^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
+Future breakage diagnostic:
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/where-allowed.rs:246:36
+   |
+LL | fn in_method_generic_param_default(_: T) {}
+   |                                    ^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
diff --git a/tests/ui/imports/import-from-missing-star-2.stderr b/tests/ui/imports/import-from-missing-star-2.stderr
index 59b000a43822b..dd35627c68465 100644
--- a/tests/ui/imports/import-from-missing-star-2.stderr
+++ b/tests/ui/imports/import-from-missing-star-2.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `spam`
 LL |     use spam::*;
    |         ^^^^ you might be missing crate `spam`
    |
-   = help: consider adding `extern crate spam` to use the `spam` crate
+help: consider importing the `spam` crate
+   |
+LL + extern crate spam;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/import-from-missing-star-3.stderr b/tests/ui/imports/import-from-missing-star-3.stderr
index 23df6b354450b..1e2412b095975 100644
--- a/tests/ui/imports/import-from-missing-star-3.stderr
+++ b/tests/ui/imports/import-from-missing-star-3.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `spam`
 LL |     use spam::*;
    |         ^^^^ you might be missing crate `spam`
    |
-   = help: consider adding `extern crate spam` to use the `spam` crate
+help: consider importing the `spam` crate
+   |
+LL + extern crate spam;
+   |
 
 error[E0432]: unresolved import `spam`
   --> $DIR/import-from-missing-star-3.rs:27:13
@@ -12,7 +15,10 @@ error[E0432]: unresolved import `spam`
 LL |         use spam::*;
    |             ^^^^ you might be missing crate `spam`
    |
-   = help: consider adding `extern crate spam` to use the `spam` crate
+help: consider importing the `spam` crate
+   |
+LL + extern crate spam;
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/imports/import-from-missing-star.stderr b/tests/ui/imports/import-from-missing-star.stderr
index b311527bc28e4..c9bb9baeb4dde 100644
--- a/tests/ui/imports/import-from-missing-star.stderr
+++ b/tests/ui/imports/import-from-missing-star.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `spam`
 LL | use spam::*;
    |     ^^^^ you might be missing crate `spam`
    |
-   = help: consider adding `extern crate spam` to use the `spam` crate
+help: consider importing the `spam` crate
+   |
+LL + extern crate spam;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/import3.stderr b/tests/ui/imports/import3.stderr
index 06260ef9ebc75..157b5b6356687 100644
--- a/tests/ui/imports/import3.stderr
+++ b/tests/ui/imports/import3.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `main`
 LL | use main::bar;
    |     ^^^^ you might be missing crate `main`
    |
-   = help: consider adding `extern crate main` to use the `main` crate
+help: consider importing the `main` crate
+   |
+LL + extern crate main;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/issue-109343.stderr b/tests/ui/imports/issue-109343.stderr
index fe06eddeada69..e66528e8df528 100644
--- a/tests/ui/imports/issue-109343.stderr
+++ b/tests/ui/imports/issue-109343.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `unresolved`
 LL | pub use unresolved::f;
    |         ^^^^^^^^^^ you might be missing crate `unresolved`
    |
-   = help: consider adding `extern crate unresolved` to use the `unresolved` crate
+help: consider importing the `unresolved` crate
+   |
+LL + extern crate unresolved;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/issue-1697.rs b/tests/ui/imports/issue-1697.rs
index 8ec48d4d28628..019237611df0d 100644
--- a/tests/ui/imports/issue-1697.rs
+++ b/tests/ui/imports/issue-1697.rs
@@ -3,6 +3,6 @@
 use unresolved::*;
 //~^ ERROR unresolved import `unresolved` [E0432]
 //~| NOTE you might be missing crate `unresolved`
-//~| HELP consider adding `extern crate unresolved` to use the `unresolved` crate
+//~| HELP consider importing the `unresolved` crate
 
 fn main() {}
diff --git a/tests/ui/imports/issue-1697.stderr b/tests/ui/imports/issue-1697.stderr
index df2957b8f2b13..ec0d94bd672f9 100644
--- a/tests/ui/imports/issue-1697.stderr
+++ b/tests/ui/imports/issue-1697.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `unresolved`
 LL | use unresolved::*;
    |     ^^^^^^^^^^ you might be missing crate `unresolved`
    |
-   = help: consider adding `extern crate unresolved` to use the `unresolved` crate
+help: consider importing the `unresolved` crate
+   |
+LL + extern crate unresolved;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/issue-33464.stderr b/tests/ui/imports/issue-33464.stderr
index 17cc0e4469e43..28fbcee401f91 100644
--- a/tests/ui/imports/issue-33464.stderr
+++ b/tests/ui/imports/issue-33464.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `abc`
 LL | use abc::one_el;
    |     ^^^ you might be missing crate `abc`
    |
-   = help: consider adding `extern crate abc` to use the `abc` crate
+help: consider importing the `abc` crate
+   |
+LL + extern crate abc;
+   |
 
 error[E0432]: unresolved import `abc`
   --> $DIR/issue-33464.rs:5:5
@@ -12,7 +15,10 @@ error[E0432]: unresolved import `abc`
 LL | use abc::{a, bbb, cccccc};
    |     ^^^ you might be missing crate `abc`
    |
-   = help: consider adding `extern crate abc` to use the `abc` crate
+help: consider importing the `abc` crate
+   |
+LL + extern crate abc;
+   |
 
 error[E0432]: unresolved import `a_very_long_name`
   --> $DIR/issue-33464.rs:7:5
@@ -20,7 +26,10 @@ error[E0432]: unresolved import `a_very_long_name`
 LL | use a_very_long_name::{el, el2};
    |     ^^^^^^^^^^^^^^^^ you might be missing crate `a_very_long_name`
    |
-   = help: consider adding `extern crate a_very_long_name` to use the `a_very_long_name` crate
+help: consider importing the `a_very_long_name` crate
+   |
+LL + extern crate a_very_long_name;
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/imports/issue-36881.stderr b/tests/ui/imports/issue-36881.stderr
index 3c136df83fe4c..004836e072c5e 100644
--- a/tests/ui/imports/issue-36881.stderr
+++ b/tests/ui/imports/issue-36881.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `issue_36881_aux`
 LL |     use issue_36881_aux::Foo;
    |         ^^^^^^^^^^^^^^^ you might be missing crate `issue_36881_aux`
    |
-   = help: consider adding `extern crate issue_36881_aux` to use the `issue_36881_aux` crate
+help: consider importing the `issue_36881_aux` crate
+   |
+LL + extern crate issue_36881_aux;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/issue-37887.stderr b/tests/ui/imports/issue-37887.stderr
index 36020707405f1..02c2c80326217 100644
--- a/tests/ui/imports/issue-37887.stderr
+++ b/tests/ui/imports/issue-37887.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `test`
 LL |     use test::*;
    |         ^^^^ you might be missing crate `test`
    |
-   = help: consider adding `extern crate test` to use the `test` crate
+help: consider importing the `test` crate
+   |
+LL + extern crate test;
+   |
 
 error[E0658]: use of unstable library feature 'test'
   --> $DIR/issue-37887.rs:2:5
diff --git a/tests/ui/imports/issue-53269.stderr b/tests/ui/imports/issue-53269.stderr
index 317b3c633a65f..d25d85bf46f02 100644
--- a/tests/ui/imports/issue-53269.stderr
+++ b/tests/ui/imports/issue-53269.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `nonexistent_module`
 LL |     use nonexistent_module::mac;
    |         ^^^^^^^^^^^^^^^^^^ you might be missing crate `nonexistent_module`
    |
-   = help: consider adding `extern crate nonexistent_module` to use the `nonexistent_module` crate
+help: consider importing the `nonexistent_module` crate
+   |
+LL + extern crate nonexistent_module;
+   |
 
 error[E0659]: `mac` is ambiguous
   --> $DIR/issue-53269.rs:8:5
diff --git a/tests/ui/imports/issue-55457.stderr b/tests/ui/imports/issue-55457.stderr
index e9126e6575c0d..9c99b6a20de7c 100644
--- a/tests/ui/imports/issue-55457.stderr
+++ b/tests/ui/imports/issue-55457.stderr
@@ -13,7 +13,10 @@ error[E0432]: unresolved import `non_existent`
 LL | use non_existent::non_existent;
    |     ^^^^^^^^^^^^ you might be missing crate `non_existent`
    |
-   = help: consider adding `extern crate non_existent` to use the `non_existent` crate
+help: consider importing the `non_existent` crate
+   |
+LL + extern crate non_existent;
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/imports/issue-55884-2.stderr b/tests/ui/imports/issue-55884-2.stderr
index 8a9d5f2a6d8a6..0d4f01aeafc64 100644
--- a/tests/ui/imports/issue-55884-2.stderr
+++ b/tests/ui/imports/issue-55884-2.stderr
@@ -24,10 +24,6 @@ note: ...and refers to the struct `ParseOptions` which is defined here
    |
 LL |     pub struct ParseOptions {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^ you could import this directly
-help: import `ParseOptions` through the re-export
-   |
-LL | pub use parser::ParseOptions;
-   |         ~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/issue-81413.stderr b/tests/ui/imports/issue-81413.stderr
index 321b3695d2cc0..aa1246c1d2f50 100644
--- a/tests/ui/imports/issue-81413.stderr
+++ b/tests/ui/imports/issue-81413.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `doesnt_exist`
 LL | pub use doesnt_exist::*;
    |         ^^^^^^^^^^^^ you might be missing crate `doesnt_exist`
    |
-   = help: consider adding `extern crate doesnt_exist` to use the `doesnt_exist` crate
+help: consider importing the `doesnt_exist` crate
+   |
+LL + extern crate doesnt_exist;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/redundant-import-extern-prelude.rs b/tests/ui/imports/redundant-import-extern-prelude.rs
index f1de06417aa5d..0064eaa931882 100644
--- a/tests/ui/imports/redundant-import-extern-prelude.rs
+++ b/tests/ui/imports/redundant-import-extern-prelude.rs
@@ -1,16 +1,17 @@
-//@ check-pass
 // Check that we detect imports that are redundant due to the extern prelude
 // and that we emit a reasonable diagnostic.
 // issue: rust-lang/rust#121915
+//~^^^ NOTE the item `aux_issue_121915` is already defined by the extern prelude
 
 // See also the discussion in .
 
 //@ compile-flags: --extern aux_issue_121915 --edition 2018
 //@ aux-build: aux-issue-121915.rs
 
-#[deny(unused_imports)]
+#[deny(redundant_imports)]
+//~^ NOTE the lint level is defined here
 fn main() {
     use aux_issue_121915;
-    //FIXME(unused_imports): ~^ ERROR the item `aux_issue_121915` is imported redundantly
+    //~^ ERROR the item `aux_issue_121915` is imported redundantly
     aux_issue_121915::item();
 }
diff --git a/tests/ui/imports/redundant-import-extern-prelude.stderr b/tests/ui/imports/redundant-import-extern-prelude.stderr
new file mode 100644
index 0000000000000..6d2518c1284b6
--- /dev/null
+++ b/tests/ui/imports/redundant-import-extern-prelude.stderr
@@ -0,0 +1,14 @@
+error: the item `aux_issue_121915` is imported redundantly
+  --> $DIR/redundant-import-extern-prelude.rs:14:9
+   |
+LL |     use aux_issue_121915;
+   |         ^^^^^^^^^^^^^^^^ the item `aux_issue_121915` is already defined by the extern prelude
+   |
+note: the lint level is defined here
+  --> $DIR/redundant-import-extern-prelude.rs:11:8
+   |
+LL | #[deny(redundant_imports)]
+   |        ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.rs b/tests/ui/imports/redundant-import-issue-121915-2015.rs
index be3b8209ada99..dc499bc40b616 100644
--- a/tests/ui/imports/redundant-import-issue-121915-2015.rs
+++ b/tests/ui/imports/redundant-import-issue-121915-2015.rs
@@ -1,12 +1,11 @@
-//@ check-pass
 //@ compile-flags: --extern aux_issue_121915 --edition 2015
 //@ aux-build: aux-issue-121915.rs
 
 extern crate aux_issue_121915;
 
-#[deny(unused_imports)]
+#[deny(redundant_imports)]
 fn main() {
     use aux_issue_121915;
-    //FIXME(unused_imports): ~^ ERROR the item `aux_issue_121915` is imported redundantly
+    //~^ ERROR the item `aux_issue_121915` is imported redundantly
     aux_issue_121915::item();
 }
diff --git a/tests/ui/imports/redundant-import-issue-121915-2015.stderr b/tests/ui/imports/redundant-import-issue-121915-2015.stderr
new file mode 100644
index 0000000000000..f4e9f604896ab
--- /dev/null
+++ b/tests/ui/imports/redundant-import-issue-121915-2015.stderr
@@ -0,0 +1,17 @@
+error: the item `aux_issue_121915` is imported redundantly
+  --> $DIR/redundant-import-issue-121915-2015.rs:8:9
+   |
+LL | extern crate aux_issue_121915;
+   | ------------------------------ the item `aux_issue_121915` is already imported here
+...
+LL |     use aux_issue_121915;
+   |         ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/redundant-import-issue-121915-2015.rs:6:8
+   |
+LL | #[deny(redundant_imports)]
+   |        ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/redundant-import-lang-prelude-attr.rs b/tests/ui/imports/redundant-import-lang-prelude-attr.rs
index 59cd570f44c25..f7ee91880bad5 100644
--- a/tests/ui/imports/redundant-import-lang-prelude-attr.rs
+++ b/tests/ui/imports/redundant-import-lang-prelude-attr.rs
@@ -1,6 +1,6 @@
-//@ check-pass
 // Check that we detect imports (of built-in attributes) that are redundant due to
 // the language prelude and that we emit a reasonable diagnostic.
+//~^^ NOTE the item `allow` is already defined by the extern prelude
 
 // Note that we use the term "extern prelude" in the label even though "language prelude"
 // would be more correct. However, it's not worth special-casing this.
@@ -9,9 +9,10 @@
 
 //@ edition: 2018
 
-#![deny(unused_imports)]
+#![deny(redundant_imports)]
+//~^ NOTE the lint level is defined here
 
-use allow; //FIXME(unused_imports): ~ ERROR the item `allow` is imported redundantly
+use allow; //~ ERROR the item `allow` is imported redundantly
 
 #[allow(unused)]
 fn main() {}
diff --git a/tests/ui/imports/redundant-import-lang-prelude-attr.stderr b/tests/ui/imports/redundant-import-lang-prelude-attr.stderr
new file mode 100644
index 0000000000000..f0016f5fab41f
--- /dev/null
+++ b/tests/ui/imports/redundant-import-lang-prelude-attr.stderr
@@ -0,0 +1,14 @@
+error: the item `allow` is imported redundantly
+  --> $DIR/redundant-import-lang-prelude-attr.rs:15:5
+   |
+LL | use allow;
+   |     ^^^^^ the item `allow` is already defined by the extern prelude
+   |
+note: the lint level is defined here
+  --> $DIR/redundant-import-lang-prelude-attr.rs:12:9
+   |
+LL | #![deny(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/redundant-import-lang-prelude.rs b/tests/ui/imports/redundant-import-lang-prelude.rs
index 53d3b70996342..3f95ffbe02e7e 100644
--- a/tests/ui/imports/redundant-import-lang-prelude.rs
+++ b/tests/ui/imports/redundant-import-lang-prelude.rs
@@ -1,16 +1,17 @@
-//@ check-pass
 // Check that we detect imports that are redundant due to the language prelude
 // and that we emit a reasonable diagnostic.
+//~^^ NOTE the item `u8` is already defined by the extern prelude
 
 // Note that we use the term "extern prelude" in the label even though "language prelude"
 // would be more correct. However, it's not worth special-casing this.
 
 // See also the discussion in .
 
-#![deny(unused_imports)]
+#![deny(redundant_imports)]
+//~^ NOTE the lint level is defined here
 
 use std::primitive::u8;
-//FIXME(unused_imports): ~^ ERROR the item `u8` is imported redundantly
+//~^ ERROR the item `u8` is imported redundantly
 
 const _: u8 = 0;
 
diff --git a/tests/ui/imports/redundant-import-lang-prelude.stderr b/tests/ui/imports/redundant-import-lang-prelude.stderr
new file mode 100644
index 0000000000000..4fd576397812e
--- /dev/null
+++ b/tests/ui/imports/redundant-import-lang-prelude.stderr
@@ -0,0 +1,14 @@
+error: the item `u8` is imported redundantly
+  --> $DIR/redundant-import-lang-prelude.rs:13:5
+   |
+LL | use std::primitive::u8;
+   |     ^^^^^^^^^^^^^^^^^^ the item `u8` is already defined by the extern prelude
+   |
+note: the lint level is defined here
+  --> $DIR/redundant-import-lang-prelude.rs:10:9
+   |
+LL | #![deny(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/suggest-remove-issue-121315.rs b/tests/ui/imports/suggest-remove-issue-121315.rs
index 2bb82833a5bc3..ee3ceb6e3a380 100644
--- a/tests/ui/imports/suggest-remove-issue-121315.rs
+++ b/tests/ui/imports/suggest-remove-issue-121315.rs
@@ -1,20 +1,19 @@
 //@ compile-flags: --edition 2021
-
-#![deny(unused_imports)]
+#![deny(unused_imports, redundant_imports)]
 #![allow(dead_code)]
 
 fn test0() {
     // Test remove FlatUnused
     use std::convert::TryFrom;
-    //FIXME(unused_imports): ~^ ERROR the item `TryFrom` is imported redundantly
+    //~^ ERROR the item `TryFrom` is imported redundantly
     let _ = u32::try_from(5i32);
 }
 
 fn test1() {
     // FIXME(yukang) Test remove NestedFullUnused
     use std::convert::{TryFrom, TryInto};
-    //FIXME(unused_imports): ~^ ERROR the item `TryFrom` is imported redundantly
-    //FIXME(unused_imports): ~| ERROR the item `TryInto` is imported redundantly
+    //~^ ERROR the item `TryFrom` is imported redundantly
+    //~| ERROR the item `TryInto` is imported redundantly
 
     let _ = u32::try_from(5i32);
     let _a: i32 = u32::try_into(5u32).unwrap();
@@ -24,7 +23,7 @@ fn test2() {
     // FIXME(yukang): Test remove both redundant and unused
     use std::convert::{AsMut, Into};
     //~^ ERROR unused import: `AsMut`
-    //FIXME(unused_imports): ~| ERROR the item `Into` is imported redundantly
+    //~| ERROR the item `Into` is imported redundantly
 
     let _a: u32 = (5u8).into();
 }
diff --git a/tests/ui/imports/suggest-remove-issue-121315.stderr b/tests/ui/imports/suggest-remove-issue-121315.stderr
index 5701514e1bd6e..5d0bf9bea6a94 100644
--- a/tests/ui/imports/suggest-remove-issue-121315.stderr
+++ b/tests/ui/imports/suggest-remove-issue-121315.stderr
@@ -1,20 +1,62 @@
+error: the item `TryFrom` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:7:9
+   |
+LL |     use std::convert::TryFrom;
+   |         ^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryFrom` is already defined here
+   |
+note: the lint level is defined here
+  --> $DIR/suggest-remove-issue-121315.rs:2:25
+   |
+LL | #![deny(unused_imports, redundant_imports)]
+   |                         ^^^^^^^^^^^^^^^^^
+
+error: the item `TryFrom` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:14:24
+   |
+LL |     use std::convert::{TryFrom, TryInto};
+   |                        ^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryFrom` is already defined here
+
+error: the item `TryInto` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:14:33
+   |
+LL |     use std::convert::{TryFrom, TryInto};
+   |                                 ^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryInto` is already defined here
+
 error: unused import: `AsMut`
-  --> $DIR/suggest-remove-issue-121315.rs:25:24
+  --> $DIR/suggest-remove-issue-121315.rs:24:24
    |
 LL |     use std::convert::{AsMut, Into};
    |                        ^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/suggest-remove-issue-121315.rs:3:9
+  --> $DIR/suggest-remove-issue-121315.rs:2:9
    |
-LL | #![deny(unused_imports)]
+LL | #![deny(unused_imports, redundant_imports)]
    |         ^^^^^^^^^^^^^^
 
+error: the item `Into` is imported redundantly
+  --> $DIR/suggest-remove-issue-121315.rs:24:31
+   |
+LL |     use std::convert::{AsMut, Into};
+   |                               ^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `Into` is already defined here
+
 error: unused import: `From`
-  --> $DIR/suggest-remove-issue-121315.rs:34:24
+  --> $DIR/suggest-remove-issue-121315.rs:33:24
    |
 LL |     use std::convert::{From, Infallible};
    |                        ^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui/imports/tool-mod-child.stderr b/tests/ui/imports/tool-mod-child.stderr
index 764256e76f045..ec110ccd75dbc 100644
--- a/tests/ui/imports/tool-mod-child.stderr
+++ b/tests/ui/imports/tool-mod-child.stderr
@@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `clippy`
 LL | use clippy::a::b;
    |     ^^^^^^ you might be missing crate `clippy`
    |
-   = help: consider adding `extern crate clippy` to use the `clippy` crate
+help: consider importing the `clippy` crate
+   |
+LL + extern crate clippy;
+   |
 
 error[E0432]: unresolved import `clippy`
   --> $DIR/tool-mod-child.rs:1:5
@@ -12,7 +15,10 @@ error[E0432]: unresolved import `clippy`
 LL | use clippy::a;
    |     ^^^^^^ you might be missing crate `clippy`
    |
-   = help: consider adding `extern crate clippy` to use the `clippy` crate
+help: consider importing the `clippy` crate
+   |
+LL + extern crate clippy;
+   |
 
 error[E0433]: failed to resolve: you might be missing crate `rustdoc`
   --> $DIR/tool-mod-child.rs:5:5
@@ -20,7 +26,10 @@ error[E0433]: failed to resolve: you might be missing crate `rustdoc`
 LL | use rustdoc::a::b;
    |     ^^^^^^^ you might be missing crate `rustdoc`
    |
-   = help: consider adding `extern crate rustdoc` to use the `rustdoc` crate
+help: consider importing the `rustdoc` crate
+   |
+LL + extern crate rustdoc;
+   |
 
 error[E0432]: unresolved import `rustdoc`
   --> $DIR/tool-mod-child.rs:4:5
@@ -28,7 +37,10 @@ error[E0432]: unresolved import `rustdoc`
 LL | use rustdoc::a;
    |     ^^^^^^^ you might be missing crate `rustdoc`
    |
-   = help: consider adding `extern crate rustdoc` to use the `rustdoc` crate
+help: consider importing the `rustdoc` crate
+   |
+LL + extern crate rustdoc;
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/imports/unresolved-imports-used.stderr b/tests/ui/imports/unresolved-imports-used.stderr
index 1cbc2356320f7..4bf02ff6e3a9f 100644
--- a/tests/ui/imports/unresolved-imports-used.stderr
+++ b/tests/ui/imports/unresolved-imports-used.stderr
@@ -16,7 +16,10 @@ error[E0432]: unresolved import `foo`
 LL | use foo::bar;
    |     ^^^ you might be missing crate `foo`
    |
-   = help: consider adding `extern crate foo` to use the `foo` crate
+help: consider importing the `foo` crate
+   |
+LL + extern crate foo;
+   |
 
 error[E0432]: unresolved import `baz`
   --> $DIR/unresolved-imports-used.rs:12:5
@@ -24,7 +27,10 @@ error[E0432]: unresolved import `baz`
 LL | use baz::*;
    |     ^^^ you might be missing crate `baz`
    |
-   = help: consider adding `extern crate baz` to use the `baz` crate
+help: consider importing the `baz` crate
+   |
+LL + extern crate baz;
+   |
 
 error[E0432]: unresolved import `foo2`
   --> $DIR/unresolved-imports-used.rs:14:5
@@ -32,7 +38,10 @@ error[E0432]: unresolved import `foo2`
 LL | use foo2::bar2;
    |     ^^^^ you might be missing crate `foo2`
    |
-   = help: consider adding `extern crate foo2` to use the `foo2` crate
+help: consider importing the `foo2` crate
+   |
+LL + extern crate foo2;
+   |
 
 error[E0432]: unresolved import `baz2`
   --> $DIR/unresolved-imports-used.rs:15:5
@@ -40,7 +49,10 @@ error[E0432]: unresolved import `baz2`
 LL | use baz2::*;
    |     ^^^^ you might be missing crate `baz2`
    |
-   = help: consider adding `extern crate baz2` to use the `baz2` crate
+help: consider importing the `baz2` crate
+   |
+LL + extern crate baz2;
+   |
 
 error[E0603]: function `quz` is private
   --> $DIR/unresolved-imports-used.rs:9:10
diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs
index 0e894ef581c38..ab46fd796c535 100644
--- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs
+++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs
@@ -10,7 +10,7 @@ const RAW_EQ_PADDING: bool = unsafe {
 const RAW_EQ_PTR: bool = unsafe {
     std::intrinsics::raw_eq(&(&0), &(&1))
 //~^ ERROR evaluation of constant value failed
-//~| `raw_eq` on bytes with provenance
+//~| unable to turn pointer into integer
 };
 
 pub fn main() {
diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr
index 317466eb3226f..af16c2bc64a9d 100644
--- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr
+++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr
@@ -8,7 +8,10 @@ error[E0080]: evaluation of constant value failed
   --> $DIR/intrinsic-raw_eq-const-bad.rs:11:5
    |
 LL |     std::intrinsics::raw_eq(&(&0), &(&1))
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `raw_eq` on bytes with provenance
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer
+   |
+   = help: this code performed an operation that depends on the underlying bytes representing a pointer
+   = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/issues/issue-26812.rs b/tests/ui/issues/issue-26812.rs
index 3391ea4b350af..e0723e016b381 100644
--- a/tests/ui/issues/issue-26812.rs
+++ b/tests/ui/issues/issue-26812.rs
@@ -1,6 +1,6 @@
-#![feature(default_type_parameter_fallback)]
-
 fn avg(_: T) {}
 //~^ ERROR generic parameters with a default cannot use forward declared identifiers
+//~| ERROR defaults for type parameters
+//~| WARN previously accepted
 
 fn main() {}
diff --git a/tests/ui/issues/issue-26812.stderr b/tests/ui/issues/issue-26812.stderr
index c2a3d4b83d536..4a18b23fd8b13 100644
--- a/tests/ui/issues/issue-26812.stderr
+++ b/tests/ui/issues/issue-26812.stderr
@@ -1,9 +1,30 @@
 error[E0128]: generic parameters with a default cannot use forward declared identifiers
-  --> $DIR/issue-26812.rs:3:10
+  --> $DIR/issue-26812.rs:1:10
    |
 LL | fn avg(_: T) {}
    |          ^^^^^^^ defaulted generic parameters cannot be forward declared
 
-error: aborting due to 1 previous error
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/issue-26812.rs:1:8
+   |
+LL | fn avg(_: T) {}
+   |        ^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0128`.
+Future incompatibility report: Future breakage diagnostic:
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/issue-26812.rs:1:8
+   |
+LL | fn avg(_: T) {}
+   |        ^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs
index 89ea9fbdcd8fe..ce9ef78ffcd9c 100644
--- a/tests/ui/issues/issue-5708.rs
+++ b/tests/ui/issues/issue-5708.rs
@@ -44,7 +44,6 @@ pub trait MyTrait {
     fn dummy(&self, t: T) -> T { panic!() }
 }
 
-#[allow(dead_code)]
 pub struct MyContainer<'a, T:'a> {
     foos: Vec<&'a (dyn MyTrait+'a)> ,
 }
diff --git a/tests/ui/json/json-bom-plus-crlf-multifile.stderr b/tests/ui/json/json-bom-plus-crlf-multifile.stderr
index 2ed26f9a8a576..9c88a7e5a2564 100644
--- a/tests/ui/json/json-bom-plus-crlf-multifile.stderr
+++ b/tests/ui/json/json-bom-plus-crlf-multifile.stderr
@@ -24,7 +24,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":622,"byte_end":623,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":613,"byte_end":619,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":623,"byte_end":623,"line_start":17,"line_end":17,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":622,"byte_end":623,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":613,"byte_end":619,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":623,"byte_end":623,"line_start":17,"line_end":17,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types: expected `String`, found integer
 "}
 {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
 
@@ -52,7 +52,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":682,"byte_end":683,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":673,"byte_end":679,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":683,"byte_end":683,"line_start":19,"line_end":19,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":682,"byte_end":683,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":673,"byte_end":679,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":683,"byte_end":683,"line_start":19,"line_end":19,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types: expected `String`, found integer
 "}
 {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
 
@@ -80,7 +80,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":746,"byte_end":747,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":736,"byte_end":742,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":747,"byte_end":747,"line_start":23,"line_end":23,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":746,"byte_end":747,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":736,"byte_end":742,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":747,"byte_end":747,"line_start":23,"line_end":23,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types: expected `String`, found integer
 "}
 {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
 
@@ -108,7 +108,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":802,"byte_end":810,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":"    let s : String = (","highlight_start":22,"highlight_end":23},{"text":"    );  // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":793,"byte_end":799,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":802,"byte_end":810,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":"    let s : String = (","highlight_start":22,"highlight_end":23},{"text":"    );  // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":793,"byte_end":799,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types: expected `String`, found `()`
 "}
 {"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors
 "}
diff --git a/tests/ui/json/json-bom-plus-crlf.stderr b/tests/ui/json/json-bom-plus-crlf.stderr
index b5e6f658616c3..cd1e3665b3e58 100644
--- a/tests/ui/json/json-bom-plus-crlf.stderr
+++ b/tests/ui/json/json-bom-plus-crlf.stderr
@@ -24,7 +24,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":607,"byte_end":608,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":598,"byte_end":604,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":608,"byte_end":608,"line_start":16,"line_end":16,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":607,"byte_end":608,"line_start":16,"line_end":16,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":598,"byte_end":604,"line_start":16,"line_end":16,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":608,"byte_end":608,"line_start":16,"line_end":16,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1;  // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:16:22: error[E0308]: mismatched types: expected `String`, found integer
 "}
 {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
 
@@ -52,7 +52,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":667,"byte_end":668,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":658,"byte_end":664,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":668,"byte_end":668,"line_start":18,"line_end":18,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":667,"byte_end":668,"line_start":18,"line_end":18,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":658,"byte_end":664,"line_start":18,"line_end":18,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":668,"byte_end":668,"line_start":18,"line_end":18,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":"    let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:18:22: error[E0308]: mismatched types: expected `String`, found integer
 "}
 {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
 
@@ -80,7 +80,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":731,"byte_end":732,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":721,"byte_end":727,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":732,"byte_end":732,"line_start":22,"line_end":22,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":731,"byte_end":732,"line_start":22,"line_end":22,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":721,"byte_end":727,"line_start":21,"line_end":21,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":732,"byte_end":732,"line_start":22,"line_end":22,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1;  // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf.rs:22:1: error[E0308]: mismatched types: expected `String`, found integer
 "}
 {"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
 
@@ -108,7 +108,7 @@ This error occurs when an expression was used in a place where the compiler
 expected an expression of a different type. It can occur in several cases, the
 most common being when calling a function and passing an argument which has a
 different type than the matching type in the function declaration.
-"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":787,"byte_end":795,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":"    let s : String = (","highlight_start":22,"highlight_end":23},{"text":"    );  // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":778,"byte_end":784,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types
+"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":787,"byte_end":795,"line_start":24,"line_end":25,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":"    let s : String = (","highlight_start":22,"highlight_end":23},{"text":"    );  // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf.rs","byte_start":778,"byte_end":784,"line_start":24,"line_end":24,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":"    let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf.rs:24:22: error[E0308]: mismatched types: expected `String`, found `()`
 "}
 {"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors
 "}
diff --git a/tests/ui/json/json-short.stderr b/tests/ui/json/json-short.stderr
index a3d579cadccff..8a4a55edf68e9 100644
--- a/tests/ui/json/json-short.stderr
+++ b/tests/ui/json/json-short.stderr
@@ -13,7 +13,7 @@ If you don't know the basics of Rust, you can look at the
 [Rust Book][rust-book] to get started.
 
 [rust-book]: https://doc.rust-lang.org/book/
-"},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":63,"byte_end":63,"line_start":1,"line_end":1,"column_start":64,"column_end":64,"is_primary":true,"text":[{"text":"//@ compile-flags: --json=diagnostic-short --error-format=json","highlight_start":64,"highlight_end":64}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:1:64: error[E0601]: `main` function not found in crate `json_short`
+"},"level":"error","spans":[{"file_name":"$DIR/json-short.rs","byte_start":63,"byte_end":63,"line_start":1,"line_end":1,"column_start":64,"column_end":64,"is_primary":true,"text":[{"text":"//@ compile-flags: --json=diagnostic-short --error-format=json","highlight_start":64,"highlight_end":64}],"label":"consider adding a `main` function to `$DIR/json-short.rs`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-short.rs:1:64: error[E0601]: `main` function not found in crate `json_short`: consider adding a `main` function to `$DIR/json-short.rs`
 "}
 {"$message_type":"diagnostic","message":"aborting due to 1 previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 1 previous error
 "}
diff --git a/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr b/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr
index a647ca27f1c26..f23f855c9e8e8 100644
--- a/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr
+++ b/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr
@@ -15,7 +15,10 @@ error[E0432]: unresolved import `r#extern`
 LL | use extern::foo;
    |     ^^^^^^ you might be missing crate `r#extern`
    |
-   = help: consider adding `extern crate r#extern` to use the `r#extern` crate
+help: consider importing the `r#extern` crate
+   |
+LL + extern crate r#extern;
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/lifetimes/elided-lint-in-mod.rs b/tests/ui/lifetimes/elided-lint-in-mod.rs
new file mode 100644
index 0000000000000..afe85cb607d40
--- /dev/null
+++ b/tests/ui/lifetimes/elided-lint-in-mod.rs
@@ -0,0 +1,11 @@
+struct Foo<'a>(&'a ());
+
+fn test(_: Foo) {}
+
+#[deny(elided_lifetimes_in_paths)]
+mod w {
+    fn test2(_: super::Foo) {}
+    //~^ ERROR hidden lifetime parameters in types are deprecated
+}
+
+fn main() {}
diff --git a/tests/ui/lifetimes/elided-lint-in-mod.stderr b/tests/ui/lifetimes/elided-lint-in-mod.stderr
new file mode 100644
index 0000000000000..1fee18028c66f
--- /dev/null
+++ b/tests/ui/lifetimes/elided-lint-in-mod.stderr
@@ -0,0 +1,20 @@
+error: hidden lifetime parameters in types are deprecated
+  --> $DIR/elided-lint-in-mod.rs:7:24
+   |
+LL |     fn test2(_: super::Foo) {}
+   |                 -------^^^
+   |                 |
+   |                 expected lifetime parameter
+   |
+note: the lint level is defined here
+  --> $DIR/elided-lint-in-mod.rs:5:8
+   |
+LL | #[deny(elided_lifetimes_in_paths)]
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: indicate the anonymous lifetime
+   |
+LL |     fn test2(_: super::Foo<'_>) {}
+   |                           ++++
+
+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 e446345aedce5..ebf6f6ca403cb 100644
--- a/tests/ui/lifetimes/unusual-rib-combinations.stderr
+++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr
@@ -72,3 +72,14 @@ error: aborting due to 8 previous errors
 
 Some errors have detailed explanations: E0106, E0214, E0308, E0770.
 For more information about an error, try `rustc --explain E0106`.
+Future incompatibility report: Future breakage diagnostic:
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/unusual-rib-combinations.rs:15:6
+   |
+LL | fn c() {}
+   |      ^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
diff --git a/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs b/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs
deleted file mode 100644
index 8cd1524045b15..0000000000000
--- a/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-//@ check-pass
-
-mod ffi {
-    use super::*;
-
-    extern "C" {
-        pub fn DomPromise_AddRef(promise: *const Promise);
-        pub fn DomPromise_Release(promise: *const Promise);
-    }
-}
-
-#[repr(C)]
-#[allow(unused)]
-pub struct Promise {
-    private: [u8; 0],
-    __nosync: ::std::marker::PhantomData<::std::rc::Rc>,
-}
-
-pub unsafe trait RefCounted {
-    unsafe fn addref(&self);
-    unsafe fn release(&self);
-}
-
-unsafe impl RefCounted for Promise {
-    unsafe fn addref(&self) {
-        ffi::DomPromise_AddRef(self)
-    }
-    unsafe fn release(&self) {
-        ffi::DomPromise_Release(self)
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/issue-59003.rs b/tests/ui/lint/dead-code/issue-59003.rs
index 319cf2db1495f..e3dcaca577889 100644
--- a/tests/ui/lint/dead-code/issue-59003.rs
+++ b/tests/ui/lint/dead-code/issue-59003.rs
@@ -4,8 +4,8 @@
 
 #![deny(dead_code)]
 
-#[allow(dead_code)]
 struct Foo {
+    #[allow(dead_code)]
     inner: u32,
 }
 
diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs
index 3386dfa47470f..ddcafedf7bc5f 100644
--- a/tests/ui/lint/dead-code/lint-dead-code-1.rs
+++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs
@@ -46,10 +46,11 @@ struct SemiUsedStruct;
 impl SemiUsedStruct {
     fn la_la_la() {}
 }
-struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed
+struct StructUsedAsField;
 pub struct StructUsedInEnum;
 struct StructUsedInGeneric;
-pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
+pub struct PubStruct2 {
+    #[allow(dead_code)]
     struct_used_as_field: *const StructUsedAsField
 }
 
diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr
index b0163df8855cd..eb728b5b93055 100644
--- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr
+++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr
@@ -22,26 +22,14 @@ error: struct `PrivStruct` is never constructed
 LL | struct PrivStruct;
    |        ^^^^^^^^^^
 
-error: struct `StructUsedAsField` is never constructed
-  --> $DIR/lint-dead-code-1.rs:49:8
-   |
-LL | struct StructUsedAsField;
-   |        ^^^^^^^^^^^^^^^^^
-
-error: struct `PubStruct2` is never constructed
-  --> $DIR/lint-dead-code-1.rs:52:12
-   |
-LL | pub struct PubStruct2 {
-   |            ^^^^^^^^^^
-
 error: enum `priv_enum` is never used
-  --> $DIR/lint-dead-code-1.rs:63:6
+  --> $DIR/lint-dead-code-1.rs:64:6
    |
 LL | enum priv_enum { foo2, bar2 }
    |      ^^^^^^^^^
 
 error: variant `bar3` is never constructed
-  --> $DIR/lint-dead-code-1.rs:66:5
+  --> $DIR/lint-dead-code-1.rs:67:5
    |
 LL | enum used_enum {
    |      --------- variant in this enum
@@ -50,25 +38,25 @@ LL |     bar3
    |     ^^^^
 
 error: function `priv_fn` is never used
-  --> $DIR/lint-dead-code-1.rs:87:4
+  --> $DIR/lint-dead-code-1.rs:88:4
    |
 LL | fn priv_fn() {
    |    ^^^^^^^
 
 error: function `foo` is never used
-  --> $DIR/lint-dead-code-1.rs:92:4
+  --> $DIR/lint-dead-code-1.rs:93:4
    |
 LL | fn foo() {
    |    ^^^
 
 error: function `bar` is never used
-  --> $DIR/lint-dead-code-1.rs:97:4
+  --> $DIR/lint-dead-code-1.rs:98:4
    |
 LL | fn bar() {
    |    ^^^
 
 error: function `baz` is never used
-  --> $DIR/lint-dead-code-1.rs:101:4
+  --> $DIR/lint-dead-code-1.rs:102:4
    |
 LL | fn baz() -> impl Copy {
    |    ^^^
@@ -79,5 +67,5 @@ error: struct `Bar` is never constructed
 LL |     pub struct Bar;
    |                ^^^
 
-error: aborting due to 12 previous errors
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs
deleted file mode 100644
index 25777438456b6..0000000000000
--- a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-#![deny(dead_code)]
-
-struct Foo(u8); //~ ERROR struct `Foo` is never constructed
-
-enum Bar { //~ ERROR enum `Bar` is never used
-    Var1(u8),
-    Var2(u8),
-}
-
-pub trait Tr1 {
-    fn f1() -> Self;
-}
-
-impl Tr1 for Foo {
-    fn f1() -> Foo {
-        let f = Foo(0);
-        let Foo(tag) = f;
-        Foo(tag)
-    }
-}
-
-impl Tr1 for Bar {
-    fn f1() -> Bar {
-        let b = Bar::Var1(0);
-        let b = if let Bar::Var1(_) = b {
-            Bar::Var1(0)
-        } else {
-            Bar::Var2(0)
-        };
-        match b {
-            Bar::Var1(_) => Bar::Var2(0),
-            Bar::Var2(_) => Bar::Var1(0),
-        }
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr
deleted file mode 100644
index 7c1a4b4597755..0000000000000
--- a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: struct `Foo` is never constructed
-  --> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8
-   |
-LL | struct Foo(u8);
-   |        ^^^
-   |
-note: the lint level is defined here
-  --> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9
-   |
-LL | #![deny(dead_code)]
-   |         ^^^^^^^^^
-
-error: enum `Bar` is never used
-  --> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6
-   |
-LL | enum Bar {
-   |      ^^^
-
-error: aborting due to 2 previous errors
-
diff --git a/tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs b/tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs
deleted file mode 100644
index 43a2e43190433..0000000000000
--- a/tests/ui/lint/dead-code/not-lint-used-adt-appeared-in-pattern.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//@ check-pass
-
-#![deny(dead_code)]
-
-#[repr(u8)]
-#[derive(Copy, Clone, Debug)]
-pub enum RecordField {
-    Target = 1,
-    Level,
-    Module,
-    File,
-    Line,
-    NumArgs,
-}
-
-unsafe trait Pod {}
-
-#[repr(transparent)]
-struct RecordFieldWrapper(RecordField);
-
-unsafe impl Pod for RecordFieldWrapper {}
-
-fn try_read(buf: &[u8]) -> T {
-    unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) }
-}
-
-pub fn foo(buf: &[u8]) -> RecordField {
-    let RecordFieldWrapper(tag) = try_read(buf);
-    tag
-}
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/unconstructible-pub-struct.rs b/tests/ui/lint/dead-code/unconstructible-pub-struct.rs
deleted file mode 100644
index 2202cbb673098..0000000000000
--- a/tests/ui/lint/dead-code/unconstructible-pub-struct.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-#![feature(never_type)]
-#![deny(dead_code)]
-
-pub struct T1(!);
-pub struct T2(());
-pub struct T3(std::marker::PhantomData);
-
-pub struct T4 {
-    _x: !,
-}
-
-pub struct T5 {
-    _x: !,
-    _y: X,
-}
-
-pub struct T6 {
-    _x: (),
-}
-
-pub struct T7 {
-    _x: (),
-    _y: X,
-}
-
-pub struct T8 {
-    _x: std::marker::PhantomData,
-}
-
-pub struct T9 { //~ ERROR struct `T9` is never constructed
-    _x: std::marker::PhantomData,
-    _y: i32,
-}
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr b/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr
deleted file mode 100644
index a3dde042bbe15..0000000000000
--- a/tests/ui/lint/dead-code/unconstructible-pub-struct.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: struct `T9` is never constructed
-  --> $DIR/unconstructible-pub-struct.rs:30:12
-   |
-LL | pub struct T9 {
-   |            ^^
-   |
-note: the lint level is defined here
-  --> $DIR/unconstructible-pub-struct.rs:2:9
-   |
-LL | #![deny(dead_code)]
-   |         ^^^^^^^^^
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs
index 658cc3d6c613a..5b755d62a0598 100644
--- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs
+++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs
@@ -1,9 +1,8 @@
 #![deny(dead_code)]
 
 struct T1; //~ ERROR struct `T1` is never constructed
-struct T2; //~ ERROR struct `T2` is never constructed
-pub struct T3(i32); //~ ERROR struct `T3` is never constructed
-pub struct T4(i32); //~ ERROR field `0` is never read
+pub struct T2(i32); //~ ERROR field `0` is never read
+struct T3;
 
 trait Trait1 { //~ ERROR trait `Trait1` is never used
     const UNUSED: i32;
@@ -12,13 +11,13 @@ trait Trait1 { //~ ERROR trait `Trait1` is never used
 }
 
 pub trait Trait2 {
-    const MAY_USED: i32;
-    fn may_used(&self) {}
+    const USED: i32;
+    fn used(&self) {}
 }
 
 pub trait Trait3 {
-    const MAY_USED: i32;
-    fn may_used() -> Self;
+    const USED: i32;
+    fn construct_self() -> Self;
 }
 
 impl Trait1 for T1 {
@@ -31,34 +30,23 @@ impl Trait1 for T1 {
 impl Trait1 for T2 {
     const UNUSED: i32 = 0;
     fn construct_self() -> Self {
-        Self
+        T2(0)
     }
 }
 
 impl Trait2 for T1 {
-    const MAY_USED: i32 = 0;
+    const USED: i32 = 0;
 }
 
 impl Trait2 for T2 {
-    const MAY_USED: i32 = 0;
-}
-
-impl Trait2 for T3 {
-    const MAY_USED: i32 = 0;
+    const USED: i32 = 0;
 }
 
-impl Trait3 for T2 {
-    const MAY_USED: i32 = 0;
-    fn may_used() -> Self {
+impl Trait3 for T3 {
+    const USED: i32 = 0;
+    fn construct_self() -> Self {
         Self
     }
 }
 
-impl Trait3 for T4 {
-    const MAY_USED: i32 = 0;
-    fn may_used() -> Self {
-        T4(0)
-    }
-}
-
 fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr
index 08c7a5cb4b062..2441a3f868dc2 100644
--- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr
+++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr
@@ -10,22 +10,10 @@ note: the lint level is defined here
 LL | #![deny(dead_code)]
    |         ^^^^^^^^^
 
-error: struct `T2` is never constructed
-  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:8
-   |
-LL | struct T2;
-   |        ^^
-
-error: struct `T3` is never constructed
-  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:12
-   |
-LL | pub struct T3(i32);
-   |            ^^
-
 error: field `0` is never read
-  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:6:15
+  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:15
    |
-LL | pub struct T4(i32);
+LL | pub struct T2(i32);
    |            -- ^^^
    |            |
    |            field in this struct
@@ -33,10 +21,10 @@ LL | pub struct T4(i32);
    = help: consider removing this field
 
 error: trait `Trait1` is never used
-  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:8:7
+  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7
    |
 LL | trait Trait1 {
    |       ^^^^^^
 
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs
deleted file mode 100644
index 36e8315ad3605..0000000000000
--- a/tests/ui/lint/dead-code/unused-assoc-const.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-#![deny(dead_code)]
-
-trait Trait {
-    const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used
-    const USED_CONST: i32;
-
-    fn foo(&self) {}
-}
-
-pub struct T(());
-
-impl Trait for T {
-    const UNUSED_CONST: i32 = 0;
-    const USED_CONST: i32 = 1;
-}
-
-fn main() {
-    T(()).foo();
-    T::USED_CONST;
-}
diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr
deleted file mode 100644
index 78296d706638b..0000000000000
--- a/tests/ui/lint/dead-code/unused-assoc-const.stderr
+++ /dev/null
@@ -1,16 +0,0 @@
-error: associated constant `UNUSED_CONST` is never used
-  --> $DIR/unused-assoc-const.rs:4:11
-   |
-LL | trait Trait {
-   |       ----- associated constant in this trait
-LL |     const UNUSED_CONST: i32;
-   |           ^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-assoc-const.rs:1:9
-   |
-LL | #![deny(dead_code)]
-   |         ^^^^^^^^^
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs
deleted file mode 100644
index 46065dcee81e0..0000000000000
--- a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-#![deny(dead_code)]
-
-struct Foo; //~ ERROR struct `Foo` is never constructed
-
-trait Trait { //~ ERROR trait `Trait` is never used
-    fn foo(&self) {}
-}
-
-impl Trait for Foo {}
-
-impl Trait for [Foo] {}
-impl Trait for [Foo; N] {}
-
-impl Trait for *const Foo {}
-impl Trait for *mut Foo {}
-
-impl Trait for &Foo {}
-impl Trait for &&Foo {}
-impl Trait for &mut Foo {}
-
-impl Trait for [&Foo] {}
-impl Trait for &[Foo] {}
-impl Trait for &*const Foo {}
-
-pub trait Trait2 {
-    fn foo(&self) {}
-}
-
-impl Trait2 for Foo {}
-
-impl Trait2 for [Foo] {}
-impl Trait2 for [Foo; N] {}
-
-impl Trait2 for *const Foo {}
-impl Trait2 for *mut Foo {}
-
-impl Trait2 for &Foo {}
-impl Trait2 for &&Foo {}
-impl Trait2 for &mut Foo {}
-
-impl Trait2 for [&Foo] {}
-impl Trait2 for &[Foo] {}
-impl Trait2 for &*const Foo {}
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr
deleted file mode 100644
index e61fc403e810d..0000000000000
--- a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: struct `Foo` is never constructed
-  --> $DIR/unused-impl-for-non-adts.rs:3:8
-   |
-LL | struct Foo;
-   |        ^^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-impl-for-non-adts.rs:1:9
-   |
-LL | #![deny(dead_code)]
-   |         ^^^^^^^^^
-
-error: trait `Trait` is never used
-  --> $DIR/unused-impl-for-non-adts.rs:5:7
-   |
-LL | trait Trait {
-   |       ^^^^^
-
-error: aborting due to 2 previous errors
-
diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs
deleted file mode 100644
index aaf4dd612de4d..0000000000000
--- a/tests/ui/lint/dead-code/unused-pub-struct.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-#![deny(dead_code)]
-
-pub struct NotLint1(());
-pub struct NotLint2(std::marker::PhantomData);
-
-pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed
-
-impl NeverConstructed {
-    pub fn not_construct_self(&self) {}
-}
-
-impl Clone for NeverConstructed {
-    fn clone(&self) -> NeverConstructed {
-        NeverConstructed(0)
-    }
-}
-
-pub trait Trait {
-    fn not_construct_self(&self);
-}
-
-impl Trait for NeverConstructed {
-    fn not_construct_self(&self) {
-        self.0;
-    }
-}
-
-pub struct Constructed(i32);
-
-impl Constructed {
-    pub fn construct_self() -> Self {
-        Constructed(0)
-    }
-}
-
-impl Clone for Constructed {
-    fn clone(&self) -> Constructed {
-        Constructed(0)
-    }
-}
-
-impl Trait for Constructed {
-    fn not_construct_self(&self) {
-        self.0;
-    }
-}
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr
deleted file mode 100644
index 3667ddb97bd3e..0000000000000
--- a/tests/ui/lint/dead-code/unused-pub-struct.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: struct `NeverConstructed` is never constructed
-  --> $DIR/unused-pub-struct.rs:6:12
-   |
-LL | pub struct NeverConstructed(i32);
-   |            ^^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-pub-struct.rs:1:9
-   |
-LL | #![deny(dead_code)]
-   |         ^^^^^^^^^
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.rs b/tests/ui/lint/dead-code/unused-struct-derive-default.rs
index f20b7cb66ee51..330ad32dd5709 100644
--- a/tests/ui/lint/dead-code/unused-struct-derive-default.rs
+++ b/tests/ui/lint/dead-code/unused-struct-derive-default.rs
@@ -22,5 +22,4 @@ pub struct T2 {
 
 fn main() {
     let _x: Used = Default::default();
-    let _e: E = Default::default();
 }
diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
index 7422f9a39f312..bbb0bd7be7064 100644
--- a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
+++ b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
@@ -4,6 +4,7 @@ error: struct `T` is never constructed
 LL | struct T;
    |        ^
    |
+   = note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
 note: the lint level is defined here
   --> $DIR/unused-struct-derive-default.rs:1:9
    |
diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs
deleted file mode 100644
index e8116d83ebf1c..0000000000000
--- a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-#![deny(dead_code)]
-
-struct T1; //~ ERROR struct `T1` is never constructed
-
-trait Foo { type Unused; } //~ ERROR trait `Foo` is never used
-impl Foo for T1 { type Unused = Self; }
-
-pub trait Bar { type Used; }
-impl Bar for T1 { type Used = Self; }
-
-fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr
deleted file mode 100644
index ab73c64063431..0000000000000
--- a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: struct `T1` is never constructed
-  --> $DIR/unused-trait-with-assoc-ty.rs:3:8
-   |
-LL | struct T1;
-   |        ^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-trait-with-assoc-ty.rs:1:9
-   |
-LL | #![deny(dead_code)]
-   |         ^^^^^^^^^
-
-error: trait `Foo` is never used
-  --> $DIR/unused-trait-with-assoc-ty.rs:5:7
-   |
-LL | trait Foo { type Unused; }
-   |       ^^^
-
-error: aborting due to 2 previous errors
-
diff --git a/tests/ui/lint/negative_literals.rs b/tests/ui/lint/negative_literals.rs
index 048fcd6ff57bc..5964bbb559a1e 100644
--- a/tests/ui/lint/negative_literals.rs
+++ b/tests/ui/lint/negative_literals.rs
@@ -1,5 +1,7 @@
 //@ check-fail
 
+#![deny(ambiguous_negative_literals)]
+
 fn main() {
     let _ = -1i32.abs();
     //~^ ERROR `-` has lower precedence than method calls
diff --git a/tests/ui/lint/negative_literals.stderr b/tests/ui/lint/negative_literals.stderr
index df000a7188255..b0323cc96a0ea 100644
--- a/tests/ui/lint/negative_literals.stderr
+++ b/tests/ui/lint/negative_literals.stderr
@@ -1,11 +1,15 @@
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:4:13
+  --> $DIR/negative_literals.rs:6:13
    |
 LL |     let _ = -1i32.abs();
    |             ^^^^^^^^^^^
    |
    = note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4`
-   = note: `#[deny(ambiguous_negative_literals)]` on by default
+note: the lint level is defined here
+  --> $DIR/negative_literals.rs:3:9
+   |
+LL | #![deny(ambiguous_negative_literals)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: add parentheses around the `-` and the literal to call the method on a negative literal
    |
 LL |     let _ = (-1i32).abs();
@@ -16,7 +20,7 @@ LL |     let _ = -(1i32.abs());
    |              +          +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:6:13
+  --> $DIR/negative_literals.rs:8:13
    |
 LL |     let _ = -1f32.abs();
    |             ^^^^^^^^^^^
@@ -32,7 +36,7 @@ LL |     let _ = -(1f32.abs());
    |              +          +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:8:13
+  --> $DIR/negative_literals.rs:10:13
    |
 LL |     let _ = -1f64.asin();
    |             ^^^^^^^^^^^^
@@ -48,7 +52,7 @@ LL |     let _ = -(1f64.asin());
    |              +           +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:10:13
+  --> $DIR/negative_literals.rs:12:13
    |
 LL |     let _ = -1f64.asinh();
    |             ^^^^^^^^^^^^^
@@ -64,7 +68,7 @@ LL |     let _ = -(1f64.asinh());
    |              +            +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:12:13
+  --> $DIR/negative_literals.rs:14:13
    |
 LL |     let _ = -1f64.tan();
    |             ^^^^^^^^^^^
@@ -80,7 +84,7 @@ LL |     let _ = -(1f64.tan());
    |              +          +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:14:13
+  --> $DIR/negative_literals.rs:16:13
    |
 LL |     let _ = -1f64.tanh();
    |             ^^^^^^^^^^^^
@@ -96,7 +100,7 @@ LL |     let _ = -(1f64.tanh());
    |              +           +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:16:13
+  --> $DIR/negative_literals.rs:18:13
    |
 LL |     let _ = -1.0_f64.cos().cos();
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -112,7 +116,7 @@ LL |     let _ = -(1.0_f64.cos().cos());
    |              +                   +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:18:13
+  --> $DIR/negative_literals.rs:20:13
    |
 LL |     let _ = -1.0_f64.cos().sin();
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -128,7 +132,7 @@ LL |     let _ = -(1.0_f64.cos().sin());
    |              +                   +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:20:13
+  --> $DIR/negative_literals.rs:22:13
    |
 LL |     let _ = -1.0_f64.sin().cos();
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +148,7 @@ LL |     let _ = -(1.0_f64.sin().cos());
    |              +                   +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:22:13
+  --> $DIR/negative_literals.rs:24:13
    |
 LL |     let _ = -1f64.sin().sin();
    |             ^^^^^^^^^^^^^^^^^
@@ -160,7 +164,7 @@ LL |     let _ = -(1f64.sin().sin());
    |              +                +
 
 error: `-` has lower precedence than method calls, which might be unexpected
-  --> $DIR/negative_literals.rs:25:11
+  --> $DIR/negative_literals.rs:27:11
    |
 LL |     dbg!( -1.0_f32.cos() );
    |           ^^^^^^^^^^^^^^
diff --git a/tests/ui/lint/unsafe_code/unsafe-extern-blocks.rs b/tests/ui/lint/unsafe_code/unsafe-extern-blocks.rs
index 6f2ead70db80c..c264fa1c8daf1 100644
--- a/tests/ui/lint/unsafe_code/unsafe-extern-blocks.rs
+++ b/tests/ui/lint/unsafe_code/unsafe-extern-blocks.rs
@@ -1,4 +1,3 @@
-#![feature(unsafe_extern_blocks)]
 #![deny(unsafe_code)]
 
 #[allow(unsafe_code)]
diff --git a/tests/ui/lint/unsafe_code/unsafe-extern-blocks.stderr b/tests/ui/lint/unsafe_code/unsafe-extern-blocks.stderr
index 5439a3112560e..6d3b064da344f 100644
--- a/tests/ui/lint/unsafe_code/unsafe-extern-blocks.stderr
+++ b/tests/ui/lint/unsafe_code/unsafe-extern-blocks.stderr
@@ -1,5 +1,5 @@
 error: usage of an `unsafe extern` block
-  --> $DIR/unsafe-extern-blocks.rs:9:1
+  --> $DIR/unsafe-extern-blocks.rs:8:1
    |
 LL | / unsafe extern "C" {
 LL | |
@@ -8,7 +8,7 @@ LL | | }
    | |_^
    |
 note: the lint level is defined here
-  --> $DIR/unsafe-extern-blocks.rs:2:9
+  --> $DIR/unsafe-extern-blocks.rs:1:9
    |
 LL | #![deny(unsafe_code)]
    |         ^^^^^^^^^^^
diff --git a/tests/ui/lint/unused/issue-59896.rs b/tests/ui/lint/unused/issue-59896.rs
index a98017524f550..5624e7f02fb43 100644
--- a/tests/ui/lint/unused/issue-59896.rs
+++ b/tests/ui/lint/unused/issue-59896.rs
@@ -1,10 +1,9 @@
-//@ check-pass
-#![deny(unused_imports)]
+#![deny(redundant_imports)]
 
 struct S;
 
 fn main() {
-    use S;  //FIXME(unused_imports): ~ ERROR the item `S` is imported redundantly
+    use S;  //~ ERROR the item `S` is imported redundantly
 
     let _s = S;
 }
diff --git a/tests/ui/lint/unused/issue-59896.stderr b/tests/ui/lint/unused/issue-59896.stderr
new file mode 100644
index 0000000000000..363a6419d9c85
--- /dev/null
+++ b/tests/ui/lint/unused/issue-59896.stderr
@@ -0,0 +1,17 @@
+error: the item `S` is imported redundantly
+  --> $DIR/issue-59896.rs:6:9
+   |
+LL | struct S;
+   | --------- the item `S` is already defined here
+...
+LL |     use S;
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-59896.rs:1:9
+   |
+LL | #![deny(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs b/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs
index 797e57f48e9d2..c8c413dccd3f2 100644
--- a/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs
+++ b/tests/ui/lint/use-redundant/use-redundant-glob-parent.rs
@@ -1,5 +1,5 @@
 //@ check-pass
-#![warn(unused_imports)]
+#![warn(redundant_imports)]
 
 pub mod bar {
     pub struct Foo(pub Bar);
@@ -9,7 +9,7 @@ pub mod bar {
 use bar::*;
 
 pub fn warning() -> Foo {
-    use bar::Foo; //FIXME(unused_imports): ~ WARNING imported redundantly
+    use bar::Foo; //~ WARNING imported redundantly
     Foo(Bar('a'))
 }
 
diff --git a/tests/ui/lint/use-redundant/use-redundant-glob-parent.stderr b/tests/ui/lint/use-redundant/use-redundant-glob-parent.stderr
new file mode 100644
index 0000000000000..3b06b2df67454
--- /dev/null
+++ b/tests/ui/lint/use-redundant/use-redundant-glob-parent.stderr
@@ -0,0 +1,17 @@
+warning: the item `Foo` is imported redundantly
+  --> $DIR/use-redundant-glob-parent.rs:12:9
+   |
+LL | use bar::*;
+   |     ------ the item `Foo` is already imported here
+...
+LL |     use bar::Foo;
+   |         ^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/use-redundant-glob-parent.rs:2:9
+   |
+LL | #![warn(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/lint/use-redundant/use-redundant-glob.rs b/tests/ui/lint/use-redundant/use-redundant-glob.rs
index e5835be89d841..52383d4ceffc3 100644
--- a/tests/ui/lint/use-redundant/use-redundant-glob.rs
+++ b/tests/ui/lint/use-redundant/use-redundant-glob.rs
@@ -1,5 +1,5 @@
 //@ check-pass
-#![warn(unused_imports)]
+#![warn(redundant_imports)]
 
 pub mod bar {
     pub struct Foo(pub Bar);
@@ -8,7 +8,7 @@ pub mod bar {
 
 pub fn warning() -> bar::Foo {
     use bar::*;
-    use bar::Foo; //FIXME(unused_imports): ~ WARNING imported redundantly
+    use bar::Foo; //~ WARNING imported redundantly
     Foo(Bar('a'))
 }
 
diff --git a/tests/ui/lint/use-redundant/use-redundant-glob.stderr b/tests/ui/lint/use-redundant/use-redundant-glob.stderr
new file mode 100644
index 0000000000000..47b2ce54bb733
--- /dev/null
+++ b/tests/ui/lint/use-redundant/use-redundant-glob.stderr
@@ -0,0 +1,16 @@
+warning: the item `Foo` is imported redundantly
+  --> $DIR/use-redundant-glob.rs:11:9
+   |
+LL |     use bar::*;
+   |         ------ the item `Foo` is already imported here
+LL |     use bar::Foo;
+   |         ^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/use-redundant-glob.rs:2:9
+   |
+LL | #![warn(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs b/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs
index 2db3435d46dbd..6a3ffa4522ae2 100644
--- a/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs
+++ b/tests/ui/lint/use-redundant/use-redundant-issue-71450.rs
@@ -1,6 +1,6 @@
 //@ check-pass
 
-#![warn(unused_imports)]
+#![warn(redundant_imports)]
 
 mod foo {
     use std::fmt;
@@ -23,8 +23,7 @@ mod foo {
 fn main() {
 
     {
-        use std::string::String;
-        //FIXME(unused_imports): ~^ WARNING the item `String` is imported redundantly
+        use std::string::String; //~ WARNING the item `String` is imported redundantly
         // 'String' from 'std::string::String'.
         let s = String::new();
         println!("{}", s);
diff --git a/tests/ui/lint/use-redundant/use-redundant-issue-71450.stderr b/tests/ui/lint/use-redundant/use-redundant-issue-71450.stderr
new file mode 100644
index 0000000000000..c14ab9e11e0fc
--- /dev/null
+++ b/tests/ui/lint/use-redundant/use-redundant-issue-71450.stderr
@@ -0,0 +1,17 @@
+warning: the item `String` is imported redundantly
+  --> $DIR/use-redundant-issue-71450.rs:26:13
+   |
+LL |         use std::string::String;
+   |             ^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `String` is already defined here
+   |
+note: the lint level is defined here
+  --> $DIR/use-redundant-issue-71450.rs:3:9
+   |
+LL | #![warn(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs
index 62f50c8a0df90..6abe3602abee7 100644
--- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs
+++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs
@@ -1,16 +1,12 @@
 //@ check-pass
-#![warn(unused_imports)]
+#![warn(redundant_imports)]
 
 
-use std::option::Option::Some;
-//FIXME(unused_imports): ~^ WARNING the item `Some` is imported redundantly
-use std::option::Option::None;
-//FIXME(unused_imports): ~ WARNING the item `None` is imported redundantly
+use std::option::Option::Some;//~ WARNING the item `Some` is imported redundantly
+use std::option::Option::None; //~ WARNING the item `None` is imported redundantly
 
-use std::result::Result::Ok;
-//FIXME(unused_imports): ~^ WARNING the item `Ok` is imported redundantly
-use std::result::Result::Err;
-//FIXME(unused_imports): ~^ WARNING the item `Err` is imported redundantly
+use std::result::Result::Ok;//~ WARNING the item `Ok` is imported redundantly
+use std::result::Result::Err;//~ WARNING the item `Err` is imported redundantly
 use std::convert::{TryFrom, TryInto};
 
 fn main() {
diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.stderr b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.stderr
new file mode 100644
index 0000000000000..2b0e16a87dc06
--- /dev/null
+++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.stderr
@@ -0,0 +1,44 @@
+warning: the item `Some` is imported redundantly
+  --> $DIR/use-redundant-prelude-rust-2015.rs:5:5
+   |
+LL | use std::option::Option::Some;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `Some` is already defined here
+   |
+note: the lint level is defined here
+  --> $DIR/use-redundant-prelude-rust-2015.rs:2:9
+   |
+LL | #![warn(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: the item `None` is imported redundantly
+  --> $DIR/use-redundant-prelude-rust-2015.rs:6:5
+   |
+LL | use std::option::Option::None;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `None` is already defined here
+
+warning: the item `Ok` is imported redundantly
+  --> $DIR/use-redundant-prelude-rust-2015.rs:8:5
+   |
+LL | use std::result::Result::Ok;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `Ok` is already defined here
+
+warning: the item `Err` is imported redundantly
+  --> $DIR/use-redundant-prelude-rust-2015.rs:9:5
+   |
+LL | use std::result::Result::Err;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `Err` is already defined here
+
+warning: 4 warnings emitted
+
diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs
index 1baa1ac1b8cd3..236ee032b677b 100644
--- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs
+++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs
@@ -1,11 +1,9 @@
 //@ check-pass
 //@ edition:2021
-#![warn(unused_imports)]
+#![warn(redundant_imports)]
 
-use std::convert::TryFrom;
-//FIXME(unused_imports): ~^ WARNING the item `TryFrom` is imported redundantly
-use std::convert::TryInto;
-//FIXME(unused_imports): ~^ WARNING the item `TryInto` is imported redundantly
+use std::convert::TryFrom;//~ WARNING the item `TryFrom` is imported redundantly
+use std::convert::TryInto;//~ WARNING the item `TryInto` is imported redundantly
 
 fn main() {
     let _e: Result = 8u8.try_into();
diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.stderr b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.stderr
new file mode 100644
index 0000000000000..526771c597aa7
--- /dev/null
+++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.stderr
@@ -0,0 +1,26 @@
+warning: the item `TryFrom` is imported redundantly
+  --> $DIR/use-redundant-prelude-rust-2021.rs:5:5
+   |
+LL | use std::convert::TryFrom;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryFrom` is already defined here
+   |
+note: the lint level is defined here
+  --> $DIR/use-redundant-prelude-rust-2021.rs:3:9
+   |
+LL | #![warn(redundant_imports)]
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: the item `TryInto` is imported redundantly
+  --> $DIR/use-redundant-prelude-rust-2021.rs:6:5
+   |
+LL | use std::convert::TryInto;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+  --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL
+   |
+   = note: the item `TryInto` is already defined here
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/lint/use-redundant/use-redundant.rs b/tests/ui/lint/use-redundant/use-redundant.rs
index 9e4902af34b7d..0efed378f988b 100644
--- a/tests/ui/lint/use-redundant/use-redundant.rs
+++ b/tests/ui/lint/use-redundant/use-redundant.rs
@@ -1,5 +1,5 @@
 //@ check-pass
-#![warn(unused_imports)]
+#![warn(unused_imports, redundant_imports)]
 
 use crate::foo::Bar;
 
@@ -18,7 +18,7 @@ use m1::*; //~ WARNING unused import
 use m2::*; //~ WARNING unused import
 
 fn main() {
-    use crate::foo::Bar; //FIXME(unused_imports): ~ WARNING imported redundantly
+    use crate::foo::Bar; //~ WARNING imported redundantly
     let _a: Bar = 3;
     baz();
 
diff --git a/tests/ui/lint/use-redundant/use-redundant.stderr b/tests/ui/lint/use-redundant/use-redundant.stderr
index 224e841123748..76846a1dca98d 100644
--- a/tests/ui/lint/use-redundant/use-redundant.stderr
+++ b/tests/ui/lint/use-redundant/use-redundant.stderr
@@ -7,7 +7,7 @@ LL | use m1::*;
 note: the lint level is defined here
   --> $DIR/use-redundant.rs:2:9
    |
-LL | #![warn(unused_imports)]
+LL | #![warn(unused_imports, redundant_imports)]
    |         ^^^^^^^^^^^^^^
 
 warning: unused import: `m2::*`
@@ -16,5 +16,20 @@ warning: unused import: `m2::*`
 LL | use m2::*;
    |     ^^^^^
 
-warning: 2 warnings emitted
+warning: the item `Bar` is imported redundantly
+  --> $DIR/use-redundant.rs:21:9
+   |
+LL | use crate::foo::Bar;
+   |     --------------- the item `Bar` is already imported here
+...
+LL |     use crate::foo::Bar;
+   |         ^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/use-redundant.rs:2:25
+   |
+LL | #![warn(unused_imports, redundant_imports)]
+   |                         ^^^^^^^^^^^^^^^^^
+
+warning: 3 warnings emitted
 
diff --git a/tests/ui/lint/wide_pointer_comparisons.stderr b/tests/ui/lint/wide_pointer_comparisons.stderr
index 81a221c0ee640..7fe382393d7e1 100644
--- a/tests/ui/lint/wide_pointer_comparisons.stderr
+++ b/tests/ui/lint/wide_pointer_comparisons.stderr
@@ -74,7 +74,7 @@ LL |     let _ = PartialEq::eq(&a, &b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |     let _ = std::ptr::addr_eq(a, b);
-   |             ~~~~~~~~~~~~~~~~~~ ~  ~
+   |             ~~~~~~~~~~~~~~~~~~ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:35:13
@@ -85,7 +85,7 @@ LL |     let _ = PartialEq::ne(&a, &b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |     let _ = !std::ptr::addr_eq(a, b);
-   |             ~~~~~~~~~~~~~~~~~~~ ~  ~
+   |             ~~~~~~~~~~~~~~~~~~~ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:37:13
@@ -96,7 +96,7 @@ LL |     let _ = a.eq(&b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |     let _ = std::ptr::addr_eq(a, b);
-   |             ++++++++++++++++++ ~  ~
+   |             ++++++++++++++++++ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:39:13
@@ -107,7 +107,7 @@ LL |     let _ = a.ne(&b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |     let _ = !std::ptr::addr_eq(a, b);
-   |             +++++++++++++++++++ ~  ~
+   |             +++++++++++++++++++ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:41:13
@@ -283,7 +283,7 @@ LL |         let _ = PartialEq::eq(a, b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = std::ptr::addr_eq(*a, *b);
-   |                 ~~~~~~~~~~~~~~~~~~~ ~~~ ~
+   |                 ~~~~~~~~~~~~~~~~~~~ ~~~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:85:17
@@ -294,7 +294,7 @@ LL |         let _ = PartialEq::ne(a, b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = !std::ptr::addr_eq(*a, *b);
-   |                 ~~~~~~~~~~~~~~~~~~~~ ~~~ ~
+   |                 ~~~~~~~~~~~~~~~~~~~~ ~~~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:87:17
@@ -305,7 +305,7 @@ LL |         let _ = PartialEq::eq(&a, &b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = std::ptr::addr_eq(*a, *b);
-   |                 ~~~~~~~~~~~~~~~~~~~ ~~~ ~
+   |                 ~~~~~~~~~~~~~~~~~~~ ~~~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:89:17
@@ -316,7 +316,7 @@ LL |         let _ = PartialEq::ne(&a, &b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = !std::ptr::addr_eq(*a, *b);
-   |                 ~~~~~~~~~~~~~~~~~~~~ ~~~ ~
+   |                 ~~~~~~~~~~~~~~~~~~~~ ~~~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:91:17
@@ -327,7 +327,7 @@ LL |         let _ = a.eq(b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = std::ptr::addr_eq(*a, *b);
-   |                 +++++++++++++++++++ ~~~ ~
+   |                 +++++++++++++++++++ ~~~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:93:17
@@ -338,7 +338,7 @@ LL |         let _ = a.ne(b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = !std::ptr::addr_eq(*a, *b);
-   |                 ++++++++++++++++++++ ~~~ ~
+   |                 ++++++++++++++++++++ ~~~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:95:17
@@ -519,11 +519,11 @@ LL |         let _ = PartialEq::eq(&a, &b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = std::ptr::addr_eq(a, b);
-   |                 ~~~~~~~~~~~~~~~~~~ ~  ~
+   |                 ~~~~~~~~~~~~~~~~~~ ~
 help: use explicit `std::ptr::eq` method to compare metadata and addresses
    |
 LL |         let _ = std::ptr::eq(a, b);
-   |                 ~~~~~~~~~~~~~ ~  ~
+   |                 ~~~~~~~~~~~~~ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:133:17
@@ -534,11 +534,11 @@ LL |         let _ = PartialEq::ne(&a, &b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = !std::ptr::addr_eq(a, b);
-   |                 ~~~~~~~~~~~~~~~~~~~ ~  ~
+   |                 ~~~~~~~~~~~~~~~~~~~ ~
 help: use explicit `std::ptr::eq` method to compare metadata and addresses
    |
 LL |         let _ = !std::ptr::eq(a, b);
-   |                 ~~~~~~~~~~~~~~ ~  ~
+   |                 ~~~~~~~~~~~~~~ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:135:17
@@ -549,11 +549,11 @@ LL |         let _ = a.eq(&b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = std::ptr::addr_eq(a, b);
-   |                 ++++++++++++++++++ ~  ~
+   |                 ++++++++++++++++++ ~
 help: use explicit `std::ptr::eq` method to compare metadata and addresses
    |
 LL |         let _ = std::ptr::eq(a, b);
-   |                 +++++++++++++ ~  ~
+   |                 +++++++++++++ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:137:17
@@ -564,11 +564,11 @@ LL |         let _ = a.ne(&b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         let _ = !std::ptr::addr_eq(a, b);
-   |                 +++++++++++++++++++ ~  ~
+   |                 +++++++++++++++++++ ~
 help: use explicit `std::ptr::eq` method to compare metadata and addresses
    |
 LL |         let _ = !std::ptr::eq(a, b);
-   |                 ++++++++++++++ ~  ~
+   |                 ++++++++++++++ ~
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:142:9
@@ -594,7 +594,7 @@ LL |         cmp!(a, b);
 help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
 LL |         cmp!(std::ptr::addr_eq(a, b));
-   |              ++++++++++++++++++ ~  +
+   |              ++++++++++++++++++    +
 
 warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
   --> $DIR/wide_pointer_comparisons.rs:159:39
diff --git a/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr b/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr
new file mode 100644
index 0000000000000..335b3f613434b
--- /dev/null
+++ b/tests/ui/macros/expr_2024_underscore_expr.edi2021.stderr
@@ -0,0 +1,32 @@
+error: no rules expected the token `_`
+  --> $DIR/expr_2024_underscore_expr.rs:22:12
+   |
+LL | macro_rules! m2021 {
+   | ------------------ when calling this macro
+...
+LL |     m2021!(_);
+   |            ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$e:expr_2021`
+  --> $DIR/expr_2024_underscore_expr.rs:10:6
+   |
+LL |     ($e:expr_2021) => {
+   |      ^^^^^^^^^^^^
+
+error: no rules expected the token `_`
+  --> $DIR/expr_2024_underscore_expr.rs:23:12
+   |
+LL | macro_rules! m2024 {
+   | ------------------ when calling this macro
+...
+LL |     m2024!(_);
+   |            ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$e:expr`
+  --> $DIR/expr_2024_underscore_expr.rs:16:6
+   |
+LL |     ($e:expr) => {
+   |      ^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr b/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr
new file mode 100644
index 0000000000000..9e49f66a89ae8
--- /dev/null
+++ b/tests/ui/macros/expr_2024_underscore_expr.edi2024.stderr
@@ -0,0 +1,17 @@
+error: no rules expected the token `_`
+  --> $DIR/expr_2024_underscore_expr.rs:22:12
+   |
+LL | macro_rules! m2021 {
+   | ------------------ when calling this macro
+...
+LL |     m2021!(_);
+   |            ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$e:expr_2021`
+  --> $DIR/expr_2024_underscore_expr.rs:10:6
+   |
+LL |     ($e:expr_2021) => {
+   |      ^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/macros/expr_2024_underscore_expr.rs b/tests/ui/macros/expr_2024_underscore_expr.rs
new file mode 100644
index 0000000000000..b2129bf154f7e
--- /dev/null
+++ b/tests/ui/macros/expr_2024_underscore_expr.rs
@@ -0,0 +1,24 @@
+//@ revisions: edi2021 edi2024
+//@[edi2024]compile-flags: --edition=2024 -Z unstable-options
+//@[edi2021]compile-flags: --edition=2021
+// This test ensures that the `_` tok is considered an
+// expression on edition 2024.
+#![feature(expr_fragment_specifier_2024)]
+#![allow(incomplete_features)]
+
+macro_rules! m2021 {
+    ($e:expr_2021) => {
+        $e = 1;
+    };
+}
+
+macro_rules! m2024 {
+    ($e:expr) => {
+        $e = 1;
+    };
+}
+
+fn main() {
+    m2021!(_); //~ ERROR: no rules expected the token `_`
+    m2024!(_); //[edi2021]~ ERROR: no rules expected the token `_`
+}
diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr
index f2c6008d27eac..de19a99d388ca 100644
--- a/tests/ui/moves/move-fn-self-receiver.stderr
+++ b/tests/ui/moves/move-fn-self-receiver.stderr
@@ -101,15 +101,6 @@ LL |     mut_foo;
    |     ^^^^^^^ move out of `mut_foo` occurs here
 LL |     ret;
    |     --- borrow later used here
-   |
-note: if `Foo` implemented `Clone`, you could clone the value
-  --> $DIR/move-fn-self-receiver.rs:5:1
-   |
-LL | struct Foo;
-   | ^^^^^^^^^^ consider implementing `Clone` for this type
-...
-LL |     let ret = mut_foo.use_mut_self();
-   |               ------- you could clone this value
 
 error[E0382]: use of moved value: `rc_foo`
   --> $DIR/move-fn-self-receiver.rs:55:5
diff --git a/tests/ui/nll/closure-access-spans.stderr b/tests/ui/nll/closure-access-spans.stderr
index f789e5e9f9571..a8024a8c20b9e 100644
--- a/tests/ui/nll/closure-access-spans.stderr
+++ b/tests/ui/nll/closure-access-spans.stderr
@@ -60,9 +60,8 @@ LL |     r.use_ref();
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     let r = &x;
-LL +     let r = x.clone();
-   |
+LL |     let r = &x.clone();
+   |               ++++++++
 
 error[E0382]: borrow of moved value: `x`
   --> $DIR/closure-access-spans.rs:35:5
@@ -109,11 +108,6 @@ LL |     || *x = String::new();
    |     ^^ -- borrow occurs due to use in closure
    |     |
    |     value borrowed here after move
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |     let r = x.clone();
-   |              ++++++++
 
 error[E0382]: use of moved value: `x`
   --> $DIR/closure-access-spans.rs:50:5
@@ -126,11 +120,6 @@ LL |     || x;
    |     ^^ - use occurs due to use in closure
    |     |
    |     value used here after move
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |     let r = x.clone();
-   |              ++++++++
 
 error: aborting due to 9 previous errors
 
diff --git a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr
index 39ec45b20eae6..ae7978004576b 100644
--- a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr
+++ b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr
@@ -10,11 +10,6 @@ LL |         _ if { (|| { let bar = b; *bar = false; })();
    |                 --             - variable moved due to use in closure
    |                 |
    |                 value moved into closure here
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |         _ if { (|| { let bar = b.clone(); *bar = false; })();
-   |                                 ++++++++
 
 error[E0382]: use of moved value: `b`
   --> $DIR/issue-27282-move-match-input-into-guard.rs:24:5
@@ -28,11 +23,6 @@ LL |             (|| { let bar = b; *bar = false; })();
    |              --             - variable moved due to use in closure
    |              |
    |              value moved into closure here
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |             (|| { let bar = b.clone(); *bar = false; })();
-   |                              ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed
deleted file mode 100644
index 7692be7ccc801..0000000000000
--- a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed
+++ /dev/null
@@ -1,23 +0,0 @@
-// Issue 27282: Example 1: This sidesteps the AST checks disallowing
-// mutable borrows in match guards by hiding the mutable borrow in a
-// guard behind a move (of the ref mut pattern id) within a closure.
-//@ run-rustfix
-#![feature(if_let_guard)]
-
-fn main() {
-    match Some(&4) {
-        None => {},
-        ref mut foo
-            if { (|| { let mut bar = foo.clone(); bar.take() })(); false } => {},
-        //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
-        Some(s) => std::process::exit(*s),
-    }
-
-    match Some(&4) {
-        None => {},
-        ref mut foo
-            if let Some(()) = { (|| { let mut bar = foo.clone(); bar.take() })(); None } => {},
-        //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
-        Some(s) => std::process::exit(*s),
-    }
-}
diff --git a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs
index f3d0a184e03c5..c3b19886a5078 100644
--- a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs
+++ b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs
@@ -1,7 +1,7 @@
 // Issue 27282: Example 1: This sidesteps the AST checks disallowing
 // mutable borrows in match guards by hiding the mutable borrow in a
 // guard behind a move (of the ref mut pattern id) within a closure.
-//@ run-rustfix
+
 #![feature(if_let_guard)]
 
 fn main() {
diff --git a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr
index 7781e77894b89..e790fda4d7ca7 100644
--- a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr
+++ b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr
@@ -7,10 +7,6 @@ LL |             if { (|| { let mut bar = foo; bar.take() })(); false } => {},
    |                   `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |             if { (|| { let mut bar = foo.clone(); bar.take() })(); false } => {},
-   |                                         ++++++++
 
 error[E0507]: cannot move out of `foo` in pattern guard
   --> $DIR/issue-27282-move-ref-mut-into-guard.rs:19:34
@@ -21,10 +17,6 @@ LL |             if let Some(()) = { (|| { let mut bar = foo; bar.take() })(); N
    |                                  `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |             if let Some(()) = { (|| { let mut bar = foo.clone(); bar.take() })(); None } => {},
-   |                                                        ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-27282-mutation-in-guard.stderr b/tests/ui/nll/issue-27282-mutation-in-guard.stderr
index f73e4aaa489aa..0b5d723172c76 100644
--- a/tests/ui/nll/issue-27282-mutation-in-guard.stderr
+++ b/tests/ui/nll/issue-27282-mutation-in-guard.stderr
@@ -7,10 +7,6 @@ LL |                 (|| { let bar = foo; bar.take() })();
    |                  `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |                 (|| { let bar = foo.clone(); bar.take() })();
-   |                                    ++++++++
 
 error[E0507]: cannot move out of `foo` in pattern guard
   --> $DIR/issue-27282-mutation-in-guard.rs:20:18
@@ -21,10 +17,6 @@ LL |                 (|| { let bar = foo; bar.take() })();
    |                  `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |                 (|| { let bar = foo.clone(); bar.take() })();
-   |                                    ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr
index f4e7869bf0056..7f9cbc3c30a92 100644
--- a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr
+++ b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr
@@ -41,10 +41,6 @@ help: consider borrowing here
    |
 LL |     let p = &s.url; p
    |             +
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |     let p = s.url.clone(); p
-   |                  ++++++++
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/nll/match-guards-always-borrow.fixed b/tests/ui/nll/match-guards-always-borrow.fixed
deleted file mode 100644
index 56e743bf196a6..0000000000000
--- a/tests/ui/nll/match-guards-always-borrow.fixed
+++ /dev/null
@@ -1,66 +0,0 @@
-#![feature(if_let_guard)]
-#![allow(unused_mut)]
-//@ run-rustfix
-
-// Here is arielb1's basic example from rust-lang/rust#27282
-// that AST borrowck is flummoxed by:
-
-fn should_reject_destructive_mutate_in_guard() {
-    match Some(&4) {
-        None => {},
-        ref mut foo if {
-            (|| { let mut bar = foo.clone(); bar.take() })();
-            //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
-            false } => { },
-        Some(s) => std::process::exit(*s),
-    }
-
-    match Some(&4) {
-        None => {},
-        ref mut foo if let Some(()) = {
-            (|| { let mut bar = foo.clone(); bar.take() })();
-            //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
-            None } => { },
-        Some(s) => std::process::exit(*s),
-    }
-}
-
-// Here below is a case that needs to keep working: we only use the
-// binding via immutable-borrow in the guard, and we mutate in the arm
-// body.
-fn allow_mutate_in_arm_body() {
-    match Some(&4) {
-        None => {},
-        ref mut foo if foo.is_some() => { foo.take(); () }
-        Some(s) => std::process::exit(*s),
-    }
-
-    match Some(&4) {
-        None => {},
-        ref mut foo if let Some(_) = foo => { foo.take(); () }
-        Some(s) => std::process::exit(*s),
-    }
-}
-
-// Here below is a case that needs to keep working: we only use the
-// binding via immutable-borrow in the guard, and we move into the arm
-// body.
-fn allow_move_into_arm_body() {
-    match Some(&4) {
-        None => {},
-        mut foo if foo.is_some() => { foo.unwrap(); () }
-        Some(s) => std::process::exit(*s),
-    }
-
-    match Some(&4) {
-        None => {},
-        mut foo if let Some(_) = foo => { foo.unwrap(); () }
-        Some(s) => std::process::exit(*s),
-    }
-}
-
-fn main() {
-    should_reject_destructive_mutate_in_guard();
-    allow_mutate_in_arm_body();
-    allow_move_into_arm_body();
-}
diff --git a/tests/ui/nll/match-guards-always-borrow.rs b/tests/ui/nll/match-guards-always-borrow.rs
index 927d55c42a6e3..5271e3cfc6655 100644
--- a/tests/ui/nll/match-guards-always-borrow.rs
+++ b/tests/ui/nll/match-guards-always-borrow.rs
@@ -1,6 +1,5 @@
 #![feature(if_let_guard)]
 #![allow(unused_mut)]
-//@ run-rustfix
 
 // Here is arielb1's basic example from rust-lang/rust#27282
 // that AST borrowck is flummoxed by:
diff --git a/tests/ui/nll/match-guards-always-borrow.stderr b/tests/ui/nll/match-guards-always-borrow.stderr
index bb0c5bd4c9761..71977bd84721b 100644
--- a/tests/ui/nll/match-guards-always-borrow.stderr
+++ b/tests/ui/nll/match-guards-always-borrow.stderr
@@ -1,5 +1,5 @@
 error[E0507]: cannot move out of `foo` in pattern guard
-  --> $DIR/match-guards-always-borrow.rs:12:14
+  --> $DIR/match-guards-always-borrow.rs:11:14
    |
 LL |             (|| { let mut bar = foo; bar.take() })();
    |              ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
@@ -7,13 +7,9 @@ LL |             (|| { let mut bar = foo; bar.take() })();
    |              `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |             (|| { let mut bar = foo.clone(); bar.take() })();
-   |                                    ++++++++
 
 error[E0507]: cannot move out of `foo` in pattern guard
-  --> $DIR/match-guards-always-borrow.rs:21:14
+  --> $DIR/match-guards-always-borrow.rs:20:14
    |
 LL |             (|| { let mut bar = foo; bar.take() })();
    |              ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
@@ -21,10 +17,6 @@ LL |             (|| { let mut bar = foo; bar.take() })();
    |              `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |             (|| { let mut bar = foo.clone(); bar.take() })();
-   |                                    ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.stderr
index 534813b2d9f5a..a8a8267290def 100644
--- a/tests/ui/nll/polonius/polonius-smoke-test.stderr
+++ b/tests/ui/nll/polonius/polonius-smoke-test.stderr
@@ -27,6 +27,12 @@ LL |     let z = x;
    |             ^ move out of `x` occurs here
 LL |     y
    |     - returning this value requires that `*x` is borrowed for `'1`
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = &mut *x;
+LL +     let y = &mut x.clone();
+   |
 
 error[E0505]: cannot move out of `s` because it is borrowed
   --> $DIR/polonius-smoke-test.rs:42:5
@@ -40,6 +46,12 @@ LL |     s;
    |     ^ move out of `s` occurs here
 LL |     tmp;
    |     --- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let r = &mut *s;
+LL +     let r = &mut s.clone();
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/panics/panic-in-cleanup.run.stderr b/tests/ui/panics/panic-in-cleanup.run.stderr
index e7def11b0e948..3417d4bf1a300 100644
--- a/tests/ui/panics/panic-in-cleanup.run.stderr
+++ b/tests/ui/panics/panic-in-cleanup.run.stderr
@@ -4,6 +4,6 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 thread 'main' panicked at $DIR/panic-in-cleanup.rs:16:9:
 BOOM
 stack backtrace:
-thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
+thread 'main' panicked at core/src/panicking.rs:$LINE:$COL:
 panic in a destructor during cleanup
 thread caused non-unwinding panic. aborting.
diff --git a/tests/ui/panics/panic-in-ffi.run.stderr b/tests/ui/panics/panic-in-ffi.run.stderr
index 596355399c801..fc70847ad9a3a 100644
--- a/tests/ui/panics/panic-in-ffi.run.stderr
+++ b/tests/ui/panics/panic-in-ffi.run.stderr
@@ -1,7 +1,7 @@
 thread 'main' panicked at $DIR/panic-in-ffi.rs:12:5:
 Test
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
-thread 'main' panicked at library/core/src/panicking.rs:$LINE:$COL:
+thread 'main' panicked at core/src/panicking.rs:$LINE:$COL:
 panic in a function that cannot unwind
 stack backtrace:
 thread caused non-unwinding panic. aborting.
diff --git a/tests/ui/parser/issues/issue-105366.fixed b/tests/ui/parser/issues/issue-105366.fixed
index 95419dc07f2cc..7157b647524dd 100644
--- a/tests/ui/parser/issues/issue-105366.fixed
+++ b/tests/ui/parser/issues/issue-105366.fixed
@@ -1,6 +1,5 @@
 //@ run-rustfix
 
-#[allow(dead_code)]
 struct Foo;
 
 impl From for Foo {
diff --git a/tests/ui/parser/issues/issue-105366.rs b/tests/ui/parser/issues/issue-105366.rs
index 3278b73799125..dc3cb8b343d32 100644
--- a/tests/ui/parser/issues/issue-105366.rs
+++ b/tests/ui/parser/issues/issue-105366.rs
@@ -1,6 +1,5 @@
 //@ run-rustfix
 
-#[allow(dead_code)]
 struct Foo;
 
 fn From for Foo {
diff --git a/tests/ui/parser/issues/issue-105366.stderr b/tests/ui/parser/issues/issue-105366.stderr
index 195305a2ec889..18c04dfaf2088 100644
--- a/tests/ui/parser/issues/issue-105366.stderr
+++ b/tests/ui/parser/issues/issue-105366.stderr
@@ -1,5 +1,5 @@
 error: you might have meant to write `impl` instead of `fn`
-  --> $DIR/issue-105366.rs:6:1
+  --> $DIR/issue-105366.rs:5:1
    |
 LL | fn From for Foo {
    | ^^
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
index e9c89807fa566..a851300a9828e 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
@@ -1,8 +1,7 @@
 // Regression test for issues #100790 and #106439.
 //@ run-rustfix
 
-#[allow(dead_code)]
-pub struct Example(usize)
+pub struct Example(#[allow(dead_code)] usize)
 where
     (): Sized;
 //~^^^ ERROR where clauses are not allowed before tuple struct bodies
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
index 3bd0f51ec2cbe..10f435859f15a 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
@@ -1,11 +1,10 @@
 // Regression test for issues #100790 and #106439.
 //@ run-rustfix
 
-#[allow(dead_code)]
 pub struct Example
 where
     (): Sized,
-(usize);
+(#[allow(dead_code)] usize);
 //~^^^ ERROR where clauses are not allowed before tuple struct bodies
 
 struct _Demo
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
index 77eafa6bea336..ddbf237e8662b 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
@@ -1,23 +1,23 @@
 error: where clauses are not allowed before tuple struct bodies
-  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
+  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
    |
 LL |   pub struct Example
    |              ------- while parsing this tuple struct
 LL | / where
 LL | |     (): Sized,
    | |______________^ unexpected where clause
-LL |   (usize);
-   |   ------- the struct body
+LL |   (#[allow(dead_code)] usize);
+   |   --------------------------- the struct body
    |
 help: move the body before the where clause
    |
-LL ~ pub struct Example(usize)
+LL ~ pub struct Example(#[allow(dead_code)] usize)
 LL | where
 LL ~     (): Sized;
    |
 
 error: where clauses are not allowed before tuple struct bodies
-  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
+  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
    |
 LL |   struct _Demo
    |          ----- while parsing this tuple struct
diff --git a/tests/ui/parser/removed-syntax/removed-syntax-box.stderr b/tests/ui/parser/removed-syntax/removed-syntax-box.stderr
index 46b891587d5a6..60c39fd37c434 100644
--- a/tests/ui/parser/removed-syntax/removed-syntax-box.stderr
+++ b/tests/ui/parser/removed-syntax/removed-syntax-box.stderr
@@ -7,7 +7,7 @@ LL |     let _ = box ();
 help: use `Box::new()` instead
    |
 LL |     let _ = Box::new(());
-   |             ~~~~~~~~~~~~
+   |             ~~~~~~~~~  +
 
 error: `box_syntax` has been removed
   --> $DIR/removed-syntax-box.rs:10:13
@@ -18,7 +18,7 @@ LL |     let _ = box 1;
 help: use `Box::new()` instead
    |
 LL |     let _ = Box::new(1);
-   |             ~~~~~~~~~~~
+   |             ~~~~~~~~~ +
 
 error: `box_syntax` has been removed
   --> $DIR/removed-syntax-box.rs:11:13
@@ -29,7 +29,7 @@ LL |     let _ = box T { a: 12, b: 18 };
 help: use `Box::new()` instead
    |
 LL |     let _ = Box::new(T { a: 12, b: 18 });
-   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |             ~~~~~~~~~                  +
 
 error: `box_syntax` has been removed
   --> $DIR/removed-syntax-box.rs:12:13
@@ -40,7 +40,7 @@ LL |     let _ = box [5; 30];
 help: use `Box::new()` instead
    |
 LL |     let _ = Box::new([5; 30]);
-   |             ~~~~~~~~~~~~~~~~~
+   |             ~~~~~~~~~       +
 
 error: `box_syntax` has been removed
   --> $DIR/removed-syntax-box.rs:13:22
@@ -51,7 +51,7 @@ LL |     let _: Box<()> = box ();
 help: use `Box::new()` instead
    |
 LL |     let _: Box<()> = Box::new(());
-   |                      ~~~~~~~~~~~~
+   |                      ~~~~~~~~~  +
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/parser/unsafe-foreign-mod-2.rs b/tests/ui/parser/unsafe-foreign-mod-2.rs
index 0b63a993c5b9e..6d339cd90881f 100644
--- a/tests/ui/parser/unsafe-foreign-mod-2.rs
+++ b/tests/ui/parser/unsafe-foreign-mod-2.rs
@@ -1,8 +1,6 @@
 extern "C" unsafe {
     //~^ ERROR expected `{`, found keyword `unsafe`
-    //~| ERROR extern block cannot be declared unsafe
     unsafe fn foo();
-    //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
 }
 
 fn main() {}
diff --git a/tests/ui/parser/unsafe-foreign-mod-2.stderr b/tests/ui/parser/unsafe-foreign-mod-2.stderr
index 8bd592b5d4311..0625e3362ed72 100644
--- a/tests/ui/parser/unsafe-foreign-mod-2.stderr
+++ b/tests/ui/parser/unsafe-foreign-mod-2.stderr
@@ -4,21 +4,5 @@ error: expected `{`, found keyword `unsafe`
 LL | extern "C" unsafe {
    |            ^^^^^^ expected `{`
 
-error: extern block cannot be declared unsafe
-  --> $DIR/unsafe-foreign-mod-2.rs:1:12
-   |
-LL | extern "C" unsafe {
-   |            ^^^^^^
-   |
-   = note: see issue #123743  for more information
-   = help: add `#![feature(unsafe_extern_blocks)]` 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: items in unadorned `extern` blocks cannot have safety qualifiers
-  --> $DIR/unsafe-foreign-mod-2.rs:4:5
-   |
-LL |     unsafe fn foo();
-   |     ^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/unsafe-foreign-mod.rs b/tests/ui/parser/unsafe-foreign-mod.rs
index eab134a4a4de4..623c3bb81e458 100644
--- a/tests/ui/parser/unsafe-foreign-mod.rs
+++ b/tests/ui/parser/unsafe-foreign-mod.rs
@@ -1,5 +1,5 @@
-unsafe extern "C" {
-    //~^ ERROR extern block cannot be declared unsafe
-}
+//@ check-pass
+
+unsafe extern "C" {}
 
 fn main() {}
diff --git a/tests/ui/parser/unsafe-foreign-mod.stderr b/tests/ui/parser/unsafe-foreign-mod.stderr
deleted file mode 100644
index 60b918a89b34d..0000000000000
--- a/tests/ui/parser/unsafe-foreign-mod.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error: extern block cannot be declared unsafe
-  --> $DIR/unsafe-foreign-mod.rs:1:1
-   |
-LL | unsafe extern "C" {
-   | ^^^^^^
-   |
-   = note: see issue #123743  for more information
-   = help: add `#![feature(unsafe_extern_blocks)]` 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
-
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
index e925fe78f3393..9359244c6ebc1 100644
--- a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
@@ -333,14 +333,6 @@ LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false
    |                                                                  ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-note: if `U` implemented `Clone`, you could clone the value
-  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5
-   |
-LL |     struct U;
-   |     ^^^^^^^^ consider implementing `Clone` for this type
-...
-LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
-   |                                                                  - you could clone this value
 
 error[E0507]: cannot move out of `b` in pattern guard
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:103:66
@@ -349,14 +341,6 @@ LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false
    |                                                                  ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
-note: if `U` implemented `Clone`, you could clone the value
-  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5
-   |
-LL |     struct U;
-   |     ^^^^^^^^ consider implementing `Clone` for this type
-...
-LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
-   |                                                                  - you could clone this value
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0507]: cannot move out of `a` in pattern guard
diff --git a/tests/ui/pattern/issue-22546.rs b/tests/ui/pattern/issue-22546.rs
index d5c5b68be78d7..fd1d5fb6c4775 100644
--- a/tests/ui/pattern/issue-22546.rs
+++ b/tests/ui/pattern/issue-22546.rs
@@ -15,7 +15,7 @@ impl Foo {
     }
 }
 
-trait Tr {
+trait Tr { //~ WARN trait `Tr` is never used
     type U;
 }
 
diff --git a/tests/ui/pattern/issue-22546.stderr b/tests/ui/pattern/issue-22546.stderr
new file mode 100644
index 0000000000000..e067a95e4226c
--- /dev/null
+++ b/tests/ui/pattern/issue-22546.stderr
@@ -0,0 +1,10 @@
+warning: trait `Tr` is never used
+  --> $DIR/issue-22546.rs:18:7
+   |
+LL | trait Tr {
+   |       ^^
+   |
+   = note: `#[warn(dead_code)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/print-calling-conventions.rs b/tests/ui/print-calling-conventions.rs
new file mode 100644
index 0000000000000..302ed088142d8
--- /dev/null
+++ b/tests/ui/print-calling-conventions.rs
@@ -0,0 +1,2 @@
+//@ compile-flags: --print calling-conventions
+//@ build-pass
diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout
new file mode 100644
index 0000000000000..da67a57f420d7
--- /dev/null
+++ b/tests/ui/print-calling-conventions.stdout
@@ -0,0 +1,34 @@
+C
+C-cmse-nonsecure-call
+C-unwind
+Rust
+aapcs
+aapcs-unwind
+avr-interrupt
+avr-non-blocking-interrupt
+cdecl
+cdecl-unwind
+efiapi
+fastcall
+fastcall-unwind
+msp430-interrupt
+ptx-kernel
+riscv-interrupt-m
+riscv-interrupt-s
+rust-call
+rust-cold
+rust-intrinsic
+stdcall
+stdcall-unwind
+system
+system-unwind
+sysv64
+sysv64-unwind
+thiscall
+thiscall-unwind
+unadjusted
+vectorcall
+vectorcall-unwind
+win64
+win64-unwind
+x86-interrupt
diff --git a/tests/ui/privacy/issue-75907.stderr b/tests/ui/privacy/issue-75907.stderr
index f7cb874c2cc0d..3121cc04478a4 100644
--- a/tests/ui/privacy/issue-75907.stderr
+++ b/tests/ui/privacy/issue-75907.stderr
@@ -14,7 +14,7 @@ LL |     let Bar(x, y, Foo(z)) = make_bar();
 help: consider making the fields publicly accessible
    |
 LL |     pub(crate) struct Bar(pub u8, pub u8, pub Foo);
-   |                           ~~~     ~~~     +++
+   |                                   ~~~     +++
 
 error[E0532]: cannot match against a tuple struct which contains private fields
   --> $DIR/issue-75907.rs:15:19
diff --git a/tests/ui/privacy/privacy5.stderr b/tests/ui/privacy/privacy5.stderr
index 615b0af2762d3..ec3abe9b81629 100644
--- a/tests/ui/privacy/privacy5.stderr
+++ b/tests/ui/privacy/privacy5.stderr
@@ -53,7 +53,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `A` is private
   --> $DIR/privacy5.rs:56:12
@@ -262,7 +262,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:69:12
@@ -281,7 +281,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:70:12
@@ -300,7 +300,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:71:12
@@ -319,7 +319,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:72:18
@@ -338,7 +338,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:73:18
@@ -357,7 +357,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:74:18
@@ -376,7 +376,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `C` is private
   --> $DIR/privacy5.rs:75:18
@@ -395,7 +395,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `A` is private
   --> $DIR/privacy5.rs:83:17
@@ -452,7 +452,7 @@ LL |     pub struct C(pub isize, isize);
 help: consider making the fields publicly accessible
    |
 LL |     pub struct C(pub isize, pub isize);
-   |                  ~~~        +++
+   |                             +++
 
 error[E0603]: tuple struct constructor `A` is private
   --> $DIR/privacy5.rs:90:20
diff --git a/tests/ui/privacy/restricted/test.stderr b/tests/ui/privacy/restricted/test.stderr
index a48bb671d9f88..5deaffbdbf3f8 100644
--- a/tests/ui/privacy/restricted/test.stderr
+++ b/tests/ui/privacy/restricted/test.stderr
@@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `bad`
 LL |     pub(in bad::path) mod m1 {}
    |            ^^^ you might be missing crate `bad`
    |
-   = help: consider adding `extern crate bad` to use the `bad` crate
+help: consider importing the `bad` crate
+   |
+LL + extern crate bad;
+   |
 
 error[E0742]: visibilities can only be restricted to ancestor modules
   --> $DIR/test.rs:51:12
diff --git a/tests/ui/process/println-with-broken-pipe.run.stderr b/tests/ui/process/println-with-broken-pipe.run.stderr
index a334c0ad20473..f9d138a042414 100644
--- a/tests/ui/process/println-with-broken-pipe.run.stderr
+++ b/tests/ui/process/println-with-broken-pipe.run.stderr
@@ -1,3 +1,3 @@
-thread 'main' panicked at library/std/src/io/stdio.rs:LL:CC:
+thread 'main' panicked at std/src/io/stdio.rs:LL:CC:
 failed printing to stdout: Broken pipe (os error 32)
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
diff --git a/tests/ui/pub/pub-ident-struct-4.fixed b/tests/ui/pub/pub-ident-struct-4.fixed
index a62ece43eced3..5fedbb7243749 100644
--- a/tests/ui/pub/pub-ident-struct-4.fixed
+++ b/tests/ui/pub/pub-ident-struct-4.fixed
@@ -1,7 +1,6 @@
 //@ run-rustfix
 
-#[allow(dead_code)]
-pub struct T(String);
+pub struct T(#[allow(dead_code)] String);
 //~^ ERROR missing `struct` for struct definition
 
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-4.rs b/tests/ui/pub/pub-ident-struct-4.rs
index 0d56a31beaf1c..5c721c25a7815 100644
--- a/tests/ui/pub/pub-ident-struct-4.rs
+++ b/tests/ui/pub/pub-ident-struct-4.rs
@@ -1,7 +1,6 @@
 //@ run-rustfix
 
-#[allow(dead_code)]
-pub T(String);
+pub T(#[allow(dead_code)] String);
 //~^ ERROR missing `struct` for struct definition
 
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr
index d3072464e05cd..04965a1a73720 100644
--- a/tests/ui/pub/pub-ident-struct-4.stderr
+++ b/tests/ui/pub/pub-ident-struct-4.stderr
@@ -1,12 +1,12 @@
 error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-4.rs:4:1
+  --> $DIR/pub-ident-struct-4.rs:3:1
    |
-LL | pub T(String);
+LL | pub T(#[allow(dead_code)] String);
    | ^^^^^
    |
 help: add `struct` here to parse `T` as a struct
    |
-LL | pub struct T(String);
+LL | pub struct T(#[allow(dead_code)] String);
    |     ++++++
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs
index 67852a6f5de0d..54beed9b3ac2b 100644
--- a/tests/ui/regions/regions-issue-21422.rs
+++ b/tests/ui/regions/regions-issue-21422.rs
@@ -5,7 +5,6 @@
 
 //@ pretty-expanded FIXME #23616
 
-#[allow(dead_code)]
 pub struct P<'a> {
     _ptr: *const &'a u8,
 }
diff --git a/tests/ui/resolve/editions-crate-root-2015.stderr b/tests/ui/resolve/editions-crate-root-2015.stderr
index 74fb7e6019ff5..7a842aca0fd27 100644
--- a/tests/ui/resolve/editions-crate-root-2015.stderr
+++ b/tests/ui/resolve/editions-crate-root-2015.stderr
@@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistant`
 LL |     fn global_inner(_: ::nonexistant::Foo) {
    |                          ^^^^^^^^^^^ you might be missing crate `nonexistant`
    |
-   = help: consider adding `extern crate nonexistant` to use the `nonexistant` crate
+help: consider importing the `nonexistant` crate
+   |
+LL + extern crate nonexistant;
+   |
 
 error[E0433]: failed to resolve: you might be missing crate `nonexistant`
   --> $DIR/editions-crate-root-2015.rs:7:30
@@ -12,7 +15,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistant`
 LL |     fn crate_inner(_: crate::nonexistant::Foo) {
    |                              ^^^^^^^^^^^ you might be missing crate `nonexistant`
    |
-   = help: consider adding `extern crate nonexistant` to use the `nonexistant` crate
+help: consider importing the `nonexistant` crate
+   |
+LL + extern crate nonexistant;
+   |
 
 error[E0412]: cannot find type `nonexistant` in the crate root
   --> $DIR/editions-crate-root-2015.rs:11:25
diff --git a/tests/ui/resolve/extern-prelude-fail.stderr b/tests/ui/resolve/extern-prelude-fail.stderr
index 4c2d5abb78247..77c10f5f995bc 100644
--- a/tests/ui/resolve/extern-prelude-fail.stderr
+++ b/tests/ui/resolve/extern-prelude-fail.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `extern_prelude`
 LL |     use extern_prelude::S;
    |         ^^^^^^^^^^^^^^ you might be missing crate `extern_prelude`
    |
-   = help: consider adding `extern crate extern_prelude` to use the `extern_prelude` crate
+help: consider importing the `extern_prelude` crate
+   |
+LL + extern crate extern_prelude;
+   |
 
 error[E0433]: failed to resolve: you might be missing crate `extern_prelude`
   --> $DIR/extern-prelude-fail.rs:8:15
@@ -12,7 +15,10 @@ error[E0433]: failed to resolve: you might be missing crate `extern_prelude`
 LL |     let s = ::extern_prelude::S;
    |               ^^^^^^^^^^^^^^ you might be missing crate `extern_prelude`
    |
-   = help: consider adding `extern crate extern_prelude` to use the `extern_prelude` crate
+help: consider importing the `extern_prelude` crate
+   |
+LL + extern crate extern_prelude;
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/resolve/issue-82865.stderr b/tests/ui/resolve/issue-82865.stderr
index ce0061a2b665e..bc7e0f0798177 100644
--- a/tests/ui/resolve/issue-82865.stderr
+++ b/tests/ui/resolve/issue-82865.stderr
@@ -4,7 +4,10 @@ error[E0433]: failed to resolve: you might be missing crate `x`
 LL | use x::y::z;
    |     ^ you might be missing crate `x`
    |
-   = help: consider adding `extern crate x` to use the `x` crate
+help: consider importing the `x` crate
+   |
+LL + extern crate x;
+   |
 
 error[E0599]: no function or associated item named `z` found for struct `Box<_, _>` in the current scope
   --> $DIR/issue-82865.rs:8:10
diff --git a/tests/ui/resolve/resolve-bad-visibility.stderr b/tests/ui/resolve/resolve-bad-visibility.stderr
index 8e4757354030e..281e5afb22302 100644
--- a/tests/ui/resolve/resolve-bad-visibility.stderr
+++ b/tests/ui/resolve/resolve-bad-visibility.stderr
@@ -22,7 +22,10 @@ error[E0433]: failed to resolve: you might be missing crate `nonexistent`
 LL | pub(in nonexistent) struct G;
    |        ^^^^^^^^^^^ you might be missing crate `nonexistent`
    |
-   = help: consider adding `extern crate nonexistent` to use the `nonexistent` crate
+help: consider importing the `nonexistent` crate
+   |
+LL + extern crate nonexistent;
+   |
 
 error[E0433]: failed to resolve: you might be missing crate `too_soon`
   --> $DIR/resolve-bad-visibility.rs:8:8
@@ -30,7 +33,10 @@ error[E0433]: failed to resolve: you might be missing crate `too_soon`
 LL | pub(in too_soon) struct H;
    |        ^^^^^^^^ you might be missing crate `too_soon`
    |
-   = help: consider adding `extern crate too_soon` to use the `too_soon` crate
+help: consider importing the `too_soon` crate
+   |
+LL + extern crate too_soon;
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/rust-2024/safe-outside-extern.rs b/tests/ui/rust-2024/safe-outside-extern.rs
index 6773df5ef03b7..674b78dc571ea 100644
--- a/tests/ui/rust-2024/safe-outside-extern.rs
+++ b/tests/ui/rust-2024/safe-outside-extern.rs
@@ -1,29 +1,21 @@
-//@ revisions: gated ungated
-#![cfg_attr(gated, feature(unsafe_extern_blocks))]
-
 safe fn foo() {}
 //~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
-//[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658]
 
 safe static FOO: i32 = 1;
 //~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
-//[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658]
 
 trait Foo {
     safe fn foo();
     //~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
-    //[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658]
 }
 
 impl Foo for () {
     safe fn foo() {}
     //~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
-    //[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658]
 }
 
 type FnPtr = safe fn(i32, i32) -> i32;
 //~^ ERROR: function pointers cannot be declared with `safe` safety qualifier
-//[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658]
 
 unsafe static LOL: u8 = 0;
 //~^ ERROR: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block
diff --git a/tests/ui/rust-2024/safe-outside-extern.stderr b/tests/ui/rust-2024/safe-outside-extern.stderr
new file mode 100644
index 0000000000000..19d7c5fde0bdf
--- /dev/null
+++ b/tests/ui/rust-2024/safe-outside-extern.stderr
@@ -0,0 +1,38 @@
+error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
+  --> $DIR/safe-outside-extern.rs:1:1
+   |
+LL | safe fn foo() {}
+   | ^^^^^^^^^^^^^^^^
+
+error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
+  --> $DIR/safe-outside-extern.rs:4:1
+   |
+LL | safe static FOO: i32 = 1;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
+  --> $DIR/safe-outside-extern.rs:8:5
+   |
+LL |     safe fn foo();
+   |     ^^^^^^^^^^^^^^
+
+error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier
+  --> $DIR/safe-outside-extern.rs:13:5
+   |
+LL |     safe fn foo() {}
+   |     ^^^^^^^^^^^^^^^^
+
+error: function pointers cannot be declared with `safe` safety qualifier
+  --> $DIR/safe-outside-extern.rs:17:14
+   |
+LL | type FnPtr = safe fn(i32, i32) -> i32;
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block
+  --> $DIR/safe-outside-extern.rs:20:1
+   |
+LL | unsafe static LOL: u8 = 0;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr
index 3a99caa719b53..77554da10e60b 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe function or block
-  --> $DIR/extern-items-unsafe.rs:14:5
+  --> $DIR/extern-items-unsafe.rs:12:5
    |
 LL |     test1(TEST1);
    |     ^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     test1(TEST1);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: use of extern static is unsafe and requires unsafe function or block
-  --> $DIR/extern-items-unsafe.rs:14:11
+  --> $DIR/extern-items-unsafe.rs:12:11
    |
 LL |     test1(TEST1);
    |           ^^^^^ use of extern static
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr
index fcf937b7ac577..33b752782d599 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe block
-  --> $DIR/extern-items-unsafe.rs:14:5
+  --> $DIR/extern-items-unsafe.rs:12:5
    |
 LL |     test1(TEST1);
    |     ^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     test1(TEST1);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: use of extern static is unsafe and requires unsafe block
-  --> $DIR/extern-items-unsafe.rs:14:11
+  --> $DIR/extern-items-unsafe.rs:12:11
    |
 LL |     test1(TEST1);
    |           ^^^^^ use of extern static
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs
index ad569a256db90..721e07acca588 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs
@@ -3,8 +3,6 @@
 //@[edition2024] edition:2024
 //@[edition2024] compile-flags: -Zunstable-options
 
-#![feature(unsafe_extern_blocks)]
-
 unsafe extern "C" {
     static TEST1: i32;
     fn test1(i: i32);
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr
index d456cfc6829e1..8ef7c2caf21ee 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr
@@ -1,5 +1,5 @@
 error: extern blocks must be unsafe
-  --> $DIR/extern-items.rs:9:1
+  --> $DIR/extern-items.rs:7:1
    |
 LL | / extern "C" {
 LL | |
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs
index 16fa1bbb8a404..08805c3634765 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs
@@ -4,8 +4,6 @@
 //@[edition2024] edition:2024
 //@[edition2024] compile-flags: -Zunstable-options
 
-#![feature(unsafe_extern_blocks)]
-
 extern "C" {
     //[edition2024]~^ ERROR extern blocks must be unsafe
     static TEST1: i32;
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.rs
index 57c03e4d896be..67df8c14b39fd 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.rs
@@ -1,6 +1,3 @@
-//@ revisions: gated ungated
-#![cfg_attr(gated, feature(unsafe_extern_blocks))]
-
 trait Bar {}
 safe impl Bar for () { }
 //~^ ERROR expected one of `!` or `::`, found keyword `impl`
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.gated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.stderr
similarity index 83%
rename from tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.gated.stderr
rename to tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.stderr
index 80e7a45f57e79..f1021726b1816 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.gated.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.stderr
@@ -1,5 +1,5 @@
 error: expected one of `!` or `::`, found keyword `impl`
-  --> $DIR/safe-impl-trait.rs:5:6
+  --> $DIR/safe-impl-trait.rs:2:6
    |
 LL | safe impl Bar for () { }
    |      ^^^^ expected one of `!` or `::`
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs
index 74cd5621fce93..b0b8a8b012a6a 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs
@@ -4,8 +4,6 @@
 //@[edition2024] compile-flags: -Zunstable-options
 //@ check-pass
 
-#![feature(unsafe_extern_blocks)]
-
 unsafe extern "C" {
     safe static TEST1: i32;
     safe fn test1(i: i32);
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.rs
index e73cb45b18877..52773b4cbbbbf 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.rs
@@ -1,6 +1,3 @@
-//@ revisions: gated ungated
-#![cfg_attr(gated, feature(unsafe_extern_blocks))]
-
 safe trait Foo {}
 //~^ ERROR expected one of `!` or `::`, found keyword `trait`
 
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.stderr
new file mode 100644
index 0000000000000..1733336a797b2
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.stderr
@@ -0,0 +1,8 @@
+error: expected one of `!` or `::`, found keyword `trait`
+  --> $DIR/safe-trait.rs:1:6
+   |
+LL | safe trait Foo {}
+   |      ^^^^^ expected one of `!` or `::`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr
index e90613357b1ca..9379798728669 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr
@@ -1,5 +1,5 @@
 error: items in unadorned `extern` blocks cannot have safety qualifiers
-  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
+  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5
    |
 LL |     safe static TEST1: i32;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -10,7 +10,7 @@ LL | unsafe extern "C" {
    | ++++++
 
 error: items in unadorned `extern` blocks cannot have safety qualifiers
-  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5
+  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
    |
 LL |     safe fn test1(i: i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr
index 1207ee158cc13..e9db6006c0b58 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr
@@ -1,5 +1,5 @@
 error: extern blocks must be unsafe
-  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:1
+  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:6:1
    |
 LL | / extern "C" {
 LL | |
@@ -11,7 +11,7 @@ LL | | }
    | |_^
 
 error: items in unadorned `extern` blocks cannot have safety qualifiers
-  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
+  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:5
    |
 LL |     safe static TEST1: i32;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | unsafe extern "C" {
    | ++++++
 
 error: items in unadorned `extern` blocks cannot have safety qualifiers
-  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5
+  --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5
    |
 LL |     safe fn test1(i: i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs
index 11f55cb195f29..4badb50b26735 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs
@@ -3,8 +3,6 @@
 //@[edition2024] edition:2024
 //@[edition2024] compile-flags: -Zunstable-options
 
-#![feature(unsafe_extern_blocks)]
-
 extern "C" {
     //[edition2024]~^ ERROR extern blocks must be unsafe
     safe static TEST1: i32;
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed
index 10c19759d8aaa..f686809615fb0 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed
@@ -1,6 +1,5 @@
 //@ run-rustfix
 
-#![feature(unsafe_extern_blocks)]
 #![deny(missing_unsafe_on_extern)]
 #![allow(unused)]
 
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs
index b81e52ddc5843..00f1cbdab54ff 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs
@@ -1,6 +1,5 @@
 //@ run-rustfix
 
-#![feature(unsafe_extern_blocks)]
 #![deny(missing_unsafe_on_extern)]
 #![allow(unused)]
 
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr
index 0a3c2cd25e3fe..bb1d068ceb91b 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr
@@ -1,5 +1,5 @@
 error: extern blocks should be unsafe
-  --> $DIR/unsafe-extern-suggestion.rs:7:1
+  --> $DIR/unsafe-extern-suggestion.rs:6:1
    |
 LL |   extern "C" {
    |   ^
@@ -16,7 +16,7 @@ LL | | }
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
    = note: for more information, see issue #123743 
 note: the lint level is defined here
-  --> $DIR/unsafe-extern-suggestion.rs:4:9
+  --> $DIR/unsafe-extern-suggestion.rs:3:9
    |
 LL | #![deny(missing_unsafe_on_extern)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr
index 8bb7ffefeea9e..e3626bb497e4f 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-items.rs:20:5
+  --> $DIR/unsafe-items.rs:18:5
    |
 LL |     test1(TEST1);
    |     ^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     test1(TEST1);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: use of extern static is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-items.rs:20:11
+  --> $DIR/unsafe-items.rs:18:11
    |
 LL |     test1(TEST1);
    |           ^^^^^ use of extern static
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr
index 9a30142a632c5..89bc501b7b5a5 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe block
-  --> $DIR/unsafe-items.rs:20:5
+  --> $DIR/unsafe-items.rs:18:5
    |
 LL |     test1(TEST1);
    |     ^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,7 @@ LL |     test1(TEST1);
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: use of extern static is unsafe and requires unsafe block
-  --> $DIR/unsafe-items.rs:20:11
+  --> $DIR/unsafe-items.rs:18:11
    |
 LL |     test1(TEST1);
    |           ^^^^^ use of extern static
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs
index 9066953abc615..dc2bae892a988 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs
@@ -3,8 +3,6 @@
 //@[edition2024] edition:2024
 //@[edition2024] compile-flags: -Zunstable-options
 
-#![feature(unsafe_extern_blocks)]
-
 unsafe extern "C" {
     unsafe static TEST1: i32;
     unsafe fn test1(i: i32);
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed
index 2ff595cc44d1e..857d34eef8521 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.fixed
@@ -1,6 +1,5 @@
 //@ run-rustfix
 
-#![feature(unsafe_extern_blocks)]
 #![allow(dead_code)]
 
 unsafe extern "C" {
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs
index 6fe43f7a5b46d..edab9850d796f 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.rs
@@ -1,6 +1,5 @@
 //@ run-rustfix
 
-#![feature(unsafe_extern_blocks)]
 #![allow(dead_code)]
 
 extern "C" {
diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr
index 05d23d001ada7..073245e650b1c 100644
--- a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr
+++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-on-extern-block-issue-126756.stderr
@@ -1,5 +1,5 @@
 error: items in unadorned `extern` blocks cannot have safety qualifiers
-  --> $DIR/unsafe-on-extern-block-issue-126756.rs:7:5
+  --> $DIR/unsafe-on-extern-block-issue-126756.rs:6:5
    |
 LL |     unsafe fn foo();
    |     ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/sanitizer/cfi-can-reveal-opaques.rs b/tests/ui/sanitizer/cfi-can-reveal-opaques.rs
new file mode 100644
index 0000000000000..55988a62a8cdf
--- /dev/null
+++ b/tests/ui/sanitizer/cfi-can-reveal-opaques.rs
@@ -0,0 +1,44 @@
+//@ needs-sanitizer-cfi
+//@ compile-flags: -Ccodegen-units=1 -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
+//@ no-prefer-dynamic
+//@ only-x86_64-unknown-linux-gnu
+//@ build-pass
+
+// See comment below for why this test exists.
+
+trait Tr {
+    type Projection;
+}
+
+impl Tr for F
+where
+    F: Fn() -> U
+{
+    type Projection = U;
+}
+
+fn test, U>(b: B) -> B::Projection
+{
+    todo!()
+}
+
+fn main() {
+    fn rpit_fn() -> impl Sized {}
+
+    // When CFI runs, it tries to compute the signature of the call. This
+    // ends up giving us a signature of:
+    //     `fn test::() -> >::Projection`,
+    // where `rpit_fn` is the ZST FnDef for the function. However, we were
+    // previously using a Reveal::UserFacing param-env. This means that the
+    // `>::Projection` return type is impossible to normalize,
+    // since it would require proving `rpit_fn: Fn() -> ()`, but we cannot
+    // prove that the `impl Sized` opaque is `()` with a user-facing param-env.
+    // This leads to a normalization error, and then an ICE.
+    //
+    // Side-note:
+    // So why is the second generic of `test` "`()`", and not the
+    // `impl Sized` since we inferred it from the return type of `rpit_fn`
+    // during typeck? Well, that's because we're using the generics from the
+    // terminator of the MIR, which has had the RevealAll pass performed on it.
+    let _ = test(rpit_fn);
+}
diff --git a/tests/ui/short-error-format.stderr b/tests/ui/short-error-format.stderr
index 8a22d673b9821..1a4a6d4df88e7 100644
--- a/tests/ui/short-error-format.stderr
+++ b/tests/ui/short-error-format.stderr
@@ -1,3 +1,3 @@
-$DIR/short-error-format.rs:6:9: error[E0308]: mismatched types
-$DIR/short-error-format.rs:8:7: error[E0599]: no method named `salut` found for type `u32` in the current scope
+$DIR/short-error-format.rs:6:9: error[E0308]: mismatched types: expected `u32`, found `String`
+$DIR/short-error-format.rs:8:7: error[E0599]: no method named `salut` found for type `u32` in the current scope: method not found in `u32`
 error: aborting due to 2 previous errors
diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr
index e01e1edab5aa6..6559845c23ec5 100644
--- a/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr
+++ b/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr
@@ -17,10 +17,6 @@ error[E0282]: type annotations needed
 LL |         .sum::<_>()
    |          ^^^ cannot infer type of the type parameter `S` declared on the method `sum`
    |
-help: consider specifying the generic argument
-   |
-LL |         .sum::<_>()
-   |             ~~~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr
index 50b8ffe0114b0..6bc3ed8d24ff0 100644
--- a/tests/ui/span/send-is-not-static-std-sync.stderr
+++ b/tests/ui/span/send-is-not-static-std-sync.stderr
@@ -15,7 +15,7 @@ LL |         *lock.lock().unwrap() = &z;
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -     *lock.lock().unwrap() = &*y;
-LL +     *lock.lock().unwrap() = y.clone();
+LL +     *lock.lock().unwrap() = &y.clone();
    |
 
 error[E0597]: `z` does not live long enough
@@ -48,7 +48,7 @@ LL |         *lock.write().unwrap() = &z;
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -     *lock.write().unwrap() = &*y;
-LL +     *lock.write().unwrap() = y.clone();
+LL +     *lock.write().unwrap() = &y.clone();
    |
 
 error[E0597]: `z` does not live long enough
@@ -81,7 +81,7 @@ LL |         tx.send(&z).unwrap();
 help: consider cloning the value if the performance cost is acceptable
    |
 LL -     tx.send(&*y);
-LL +     tx.send(y.clone());
+LL +     tx.send(&y.clone());
    |
 
 error[E0597]: `z` does not live long enough
diff --git a/tests/ui/stable-mir-print/basic_function.stdout b/tests/ui/stable-mir-print/basic_function.stdout
index 3926c1048f54a..76288c2aa49f2 100644
--- a/tests/ui/stable-mir-print/basic_function.stdout
+++ b/tests/ui/stable-mir-print/basic_function.stdout
@@ -44,14 +44,14 @@ fn demux(_1: u8) -> u8 {
     let mut _0: u8;
     debug input => _1;
     bb0: {
-        switchInt(_1) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb1];
+        switchInt(_1) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1];
     }
     bb1: {
         _0 = 0_u8;
         goto -> bb5;
     }
     bb2: {
-        _0 = 10_u8;
+        _0 = 8_u8;
         goto -> bb5;
     }
     bb3: {
@@ -59,7 +59,7 @@ fn demux(_1: u8) -> u8 {
         goto -> bb5;
     }
     bb4: {
-        _0 = 8_u8;
+        _0 = 10_u8;
         goto -> bb5;
     }
     bb5: {
diff --git a/tests/ui/static/static-lifetime.rs b/tests/ui/static/static-lifetime.rs
index ce1eeb6105f09..a861a2ffedab4 100644
--- a/tests/ui/static/static-lifetime.rs
+++ b/tests/ui/static/static-lifetime.rs
@@ -1,6 +1,7 @@
 pub trait Arbitrary: Sized + 'static {}
 
 impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {} //~ ERROR lifetime bound
+//~^ ERROR cannot infer an appropriate lifetime for lifetime parameter `'a`
 
 fn main() {
 }
diff --git a/tests/ui/static/static-lifetime.stderr b/tests/ui/static/static-lifetime.stderr
index 8c9434ce3cb0b..7a956dbfeef6e 100644
--- a/tests/ui/static/static-lifetime.stderr
+++ b/tests/ui/static/static-lifetime.stderr
@@ -11,6 +11,32 @@ LL | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {}
    |      ^^
    = note: but lifetime parameter must outlive the static lifetime
 
-error: aborting due to 1 previous error
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+  --> $DIR/static-lifetime.rs:3:34
+   |
+LL | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {}
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+  --> $DIR/static-lifetime.rs:3:6
+   |
+LL | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {}
+   |      ^^
+note: ...so that the types are compatible
+  --> $DIR/static-lifetime.rs:3:34
+   |
+LL | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {}
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: expected ` as Arbitrary>`
+              found ` as Arbitrary>`
+   = note: but, the lifetime must be valid for the static lifetime...
+note: ...so that the declared lifetime parameter bounds are satisfied
+  --> $DIR/static-lifetime.rs:3:34
+   |
+LL | impl<'a, A: Clone> Arbitrary for ::std::borrow::Cow<'a, A> {}
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0478`.
+Some errors have detailed explanations: E0478, E0495.
+For more information about an error, try `rustc --explain E0478`.
diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs
index 16439a7fedd3e..19672e41c9a30 100644
--- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs
+++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs
@@ -3,10 +3,8 @@
 #![allow(unused_variables)]
 //@ pretty-expanded FIXME #23616
 
-#[allow(dead_code)]
 pub struct Fd(u32);
 
-#[allow(dead_code)]
 fn foo(a: u32) {}
 
 impl Drop for Fd {
diff --git a/tests/ui/structs-enums/uninstantiable-struct.rs b/tests/ui/structs-enums/uninstantiable-struct.rs
index 1074dbcd6e6b5..97bc7d8414e7f 100644
--- a/tests/ui/structs-enums/uninstantiable-struct.rs
+++ b/tests/ui/structs-enums/uninstantiable-struct.rs
@@ -1,5 +1,4 @@
 //@ run-pass
-#[allow(dead_code)]
-pub struct Z(&'static Z);
+pub struct Z(#[allow(dead_code)] &'static Z);
 
 pub fn main() {}
diff --git a/tests/ui/suggestions/borrow-for-loop-head.stderr b/tests/ui/suggestions/borrow-for-loop-head.stderr
index a8de9986c312d..55fcb44168c49 100644
--- a/tests/ui/suggestions/borrow-for-loop-head.stderr
+++ b/tests/ui/suggestions/borrow-for-loop-head.stderr
@@ -10,9 +10,8 @@ LL |         for j in a {
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -     for i in &a {
-LL +     for i in a.clone() {
-   |
+LL |     for i in &a.clone() {
+   |                ++++++++
 
 error[E0382]: use of moved value: `a`
   --> $DIR/borrow-for-loop-head.rs:4:18
diff --git a/tests/ui/suggestions/derive-clone-for-eq.fixed b/tests/ui/suggestions/derive-clone-for-eq.fixed
index cf800c6e47d87..4dc362f947875 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.fixed
+++ b/tests/ui/suggestions/derive-clone-for-eq.fixed
@@ -1,7 +1,6 @@
 //@ run-rustfix
 // https://github.com/rust-lang/rust/issues/79076
 
-#[allow(dead_code)]
 #[derive(Clone, Eq)] //~ ERROR [E0277]
 pub struct Struct(T);
 
diff --git a/tests/ui/suggestions/derive-clone-for-eq.rs b/tests/ui/suggestions/derive-clone-for-eq.rs
index 84736426bac0d..b3635000f1658 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.rs
+++ b/tests/ui/suggestions/derive-clone-for-eq.rs
@@ -1,7 +1,6 @@
 //@ run-rustfix
 // https://github.com/rust-lang/rust/issues/79076
 
-#[allow(dead_code)]
 #[derive(Clone, Eq)] //~ ERROR [E0277]
 pub struct Struct(T);
 
diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr
index 54670fbffcfb9..6fae6e1316df0 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.stderr
+++ b/tests/ui/suggestions/derive-clone-for-eq.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `T: Clone` is not satisfied
-  --> $DIR/derive-clone-for-eq.rs:5:17
+  --> $DIR/derive-clone-for-eq.rs:4:17
    |
 LL | #[derive(Clone, Eq)]
    |                 ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct: PartialEq`
    |
 note: required for `Struct` to implement `PartialEq`
-  --> $DIR/derive-clone-for-eq.rs:8:19
+  --> $DIR/derive-clone-for-eq.rs:7:19
    |
 LL | impl PartialEq for Struct
    |         -----     ^^^^^^^^^^^^     ^^^^^^^^^
diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed
index ef07d55871e7e..4a5a9483c20c8 100644
--- a/tests/ui/suggestions/option-content-move.fixed
+++ b/tests/ui/suggestions/option-content-move.fixed
@@ -1,5 +1,4 @@
 //@ run-rustfix
-#[allow(dead_code)]
 pub struct LipogramCorpora {
     selections: Vec<(char, Option)>,
 }
@@ -18,7 +17,6 @@ impl LipogramCorpora {
     }
 }
 
-#[allow(dead_code)]
 pub struct LipogramCorpora2 {
     selections: Vec<(char, Result)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs
index 5be6358fd6a57..90d05c7439970 100644
--- a/tests/ui/suggestions/option-content-move.rs
+++ b/tests/ui/suggestions/option-content-move.rs
@@ -1,5 +1,4 @@
 //@ run-rustfix
-#[allow(dead_code)]
 pub struct LipogramCorpora {
     selections: Vec<(char, Option)>,
 }
@@ -18,7 +17,6 @@ impl LipogramCorpora {
     }
 }
 
-#[allow(dead_code)]
 pub struct LipogramCorpora2 {
     selections: Vec<(char, Result)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr
index b4ec5b180d210..a382a04344aeb 100644
--- a/tests/ui/suggestions/option-content-move.stderr
+++ b/tests/ui/suggestions/option-content-move.stderr
@@ -1,5 +1,5 @@
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:11:20
+  --> $DIR/option-content-move.rs:10:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@@ -19,7 +19,7 @@ LL |                 if selection.1.clone().unwrap().contains(selection.0) {
    |                               ++++++++
 
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:30:20
+  --> $DIR/option-content-move.rs:28:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
diff --git a/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr b/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr
index 618ccba0d3d12..dde6060c4334b 100644
--- a/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr
+++ b/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr
@@ -40,7 +40,7 @@ LL |     let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>();
 help: surround the type parameters with angle brackets
    |
 LL |     let _ = vec![1, 2, 3].into_iter().collect::>();
-   |                                                +      ~
+   |                                                +
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/target-feature/implicit-features-cli.rs b/tests/ui/target-feature/implicit-features-cli.rs
new file mode 100644
index 0000000000000..34e7c3d506659
--- /dev/null
+++ b/tests/ui/target-feature/implicit-features-cli.rs
@@ -0,0 +1,9 @@
+//@ only-wasm32-wasip1
+//@ compile-flags: -Ctarget-feature=+relaxed-simd --crate-type=lib
+//@ build-pass
+
+use std::arch::wasm32::*;
+
+pub fn test(a: v128, b: v128, m: v128) -> v128 {
+    i64x2_relaxed_laneselect(a, b, m)
+}
diff --git a/tests/ui/target-feature/implicit-features.rs b/tests/ui/target-feature/implicit-features.rs
new file mode 100644
index 0000000000000..b9c48b0822d53
--- /dev/null
+++ b/tests/ui/target-feature/implicit-features.rs
@@ -0,0 +1,10 @@
+//@ only-wasm32-wasip1
+//@ compile-flags: --crate-type=lib
+//@ build-pass
+
+use std::arch::wasm32::*;
+
+#[target_feature(enable = "relaxed-simd")]
+pub fn test(a: v128, b: v128, m: v128) -> v128 {
+    i64x2_relaxed_laneselect(a, b, m)
+}
diff --git a/tests/ui/target-feature/wasm-relaxed-simd.rs b/tests/ui/target-feature/wasm-relaxed-simd.rs
new file mode 100644
index 0000000000000..34e7c3d506659
--- /dev/null
+++ b/tests/ui/target-feature/wasm-relaxed-simd.rs
@@ -0,0 +1,9 @@
+//@ only-wasm32-wasip1
+//@ compile-flags: -Ctarget-feature=+relaxed-simd --crate-type=lib
+//@ build-pass
+
+use std::arch::wasm32::*;
+
+pub fn test(a: v128, b: v128, m: v128) -> v128 {
+    i64x2_relaxed_laneselect(a, b, m)
+}
diff --git a/tests/ui/traits/non_lifetime_binders/shadowed.rs b/tests/ui/traits/non_lifetime_binders/shadowed.rs
new file mode 100644
index 0000000000000..1c480e3940b89
--- /dev/null
+++ b/tests/ui/traits/non_lifetime_binders/shadowed.rs
@@ -0,0 +1,18 @@
+#![feature(non_lifetime_binders)]
+//~^ WARN the feature `non_lifetime_binders` is incomplete
+
+fn function() where for (): Sized {}
+//~^ ERROR the name `T` is already used for a generic parameter
+
+struct Struct(T) where for (): Sized;
+//~^ ERROR the name `T` is already used for a generic parameter
+
+impl Struct {
+    fn method() where for (): Sized {}
+    //~^ ERROR the name `T` is already used for a generic parameter
+}
+
+fn repeated() where for (): Sized {}
+//~^ ERROR the name `T` is already used for a generic parameter
+
+fn main() {}
diff --git a/tests/ui/traits/non_lifetime_binders/shadowed.stderr b/tests/ui/traits/non_lifetime_binders/shadowed.stderr
new file mode 100644
index 0000000000000..59a073aefc961
--- /dev/null
+++ b/tests/ui/traits/non_lifetime_binders/shadowed.stderr
@@ -0,0 +1,44 @@
+error[E0403]: the name `T` is already used for a generic parameter in this item's generic parameters
+  --> $DIR/shadowed.rs:4:28
+   |
+LL | fn function() where for (): Sized {}
+   |             -              ^ already used
+   |             |
+   |             first use of `T`
+
+error[E0403]: the name `T` is already used for a generic parameter in this item's generic parameters
+  --> $DIR/shadowed.rs:7:31
+   |
+LL | struct Struct(T) where for (): Sized;
+   |               -               ^ already used
+   |               |
+   |               first use of `T`
+
+error[E0403]: the name `T` is already used for a generic parameter in this item's generic parameters
+  --> $DIR/shadowed.rs:11:27
+   |
+LL | impl Struct {
+   |      - first use of `T`
+LL |     fn method() where for (): Sized {}
+   |                           ^ already used
+
+error[E0403]: the name `T` is already used for a generic parameter in this item's generic parameters
+  --> $DIR/shadowed.rs:15:28
+   |
+LL | fn repeated() where for (): Sized {}
+   |                         -  ^ already used
+   |                         |
+   |                         first use of `T`
+
+warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/shadowed.rs:1:12
+   |
+LL | #![feature(non_lifetime_binders)]
+   |            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #108185  for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to 4 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0403`.
diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs
index 0ae562c0d30a0..462b0bc5bb77f 100644
--- a/tests/ui/traits/object/generics.rs
+++ b/tests/ui/traits/object/generics.rs
@@ -7,7 +7,6 @@ pub trait Trait2 {
     fn doit(&self) -> A;
 }
 
-#[allow(dead_code)]
 pub struct Impl {
     m1: marker::PhantomData<(A1,A2,A3)>,
     /*
diff --git a/tests/ui/try-block/try-block-bad-lifetime.stderr b/tests/ui/try-block/try-block-bad-lifetime.stderr
index 6f69329535711..28941cb0a9e40 100644
--- a/tests/ui/try-block/try-block-bad-lifetime.stderr
+++ b/tests/ui/try-block/try-block-bad-lifetime.stderr
@@ -34,11 +34,6 @@ LL |             Err(k) ?;
 ...
 LL |         ::std::mem::drop(k);
    |                          ^ value used here after move
-   |
-help: consider cloning the value if the performance cost is acceptable
-   |
-LL |             Err(k.clone()) ?;
-   |                  ++++++++
 
 error[E0506]: cannot assign to `i` because it is borrowed
   --> $DIR/try-block-bad-lifetime.rs:32:9
diff --git a/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr b/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr
index dc0bea58a70e8..bf8829c09257f 100644
--- a/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr
+++ b/tests/ui/type-inference/unbounded-type-param-in-fn-with-assoc-type.stderr
@@ -12,3 +12,13 @@ LL |     foo::();
 error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0282`.
+Future incompatibility report: Future breakage diagnostic:
+warning: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/unbounded-type-param-in-fn-with-assoc-type.rs:3:11
+   |
+LL | fn foo() -> (T, U) {
+   |           ^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+
diff --git a/tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.rs b/tests/ui/type/default_type_parameter_in_fn_or_impl.rs
similarity index 100%
rename from tests/ui/feature-gates/feature-gate-default_type_parameter_fallback.rs
rename to tests/ui/type/default_type_parameter_in_fn_or_impl.rs
diff --git a/tests/ui/type/default_type_parameter_in_fn_or_impl.stderr b/tests/ui/type/default_type_parameter_in_fn_or_impl.stderr
new file mode 100644
index 0000000000000..a3205cd3c29ca
--- /dev/null
+++ b/tests/ui/type/default_type_parameter_in_fn_or_impl.stderr
@@ -0,0 +1,43 @@
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/default_type_parameter_in_fn_or_impl.rs:3:8
+   |
+LL | fn avg(_: T) {}
+   |        ^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/default_type_parameter_in_fn_or_impl.rs:8:6
+   |
+LL | impl S {}
+   |      ^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+
+error: aborting due to 2 previous errors
+
+Future incompatibility report: Future breakage diagnostic:
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/default_type_parameter_in_fn_or_impl.rs:3:8
+   |
+LL | fn avg(_: T) {}
+   |        ^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
+Future breakage diagnostic:
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/default_type_parameter_in_fn_or_impl.rs:8:6
+   |
+LL | impl S {}
+   |      ^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #36887 
+   = note: `#[deny(invalid_type_param_default)]` on by default
+
diff --git a/tests/ui/type/pattern_types/missing-is.rs b/tests/ui/type/pattern_types/missing-is.rs
new file mode 100644
index 0000000000000..2fbcc1908efcb
--- /dev/null
+++ b/tests/ui/type/pattern_types/missing-is.rs
@@ -0,0 +1,8 @@
+#![feature(core_pattern_type, core_pattern_types)]
+
+use std::pat::pattern_type;
+
+fn main() {
+    let x: pattern_type!(i32 0..1);
+    //~^ ERROR expected one of `!`, `(`, `+`, `::`, `<`, or `is`, found `0`
+}
diff --git a/tests/ui/type/pattern_types/missing-is.stderr b/tests/ui/type/pattern_types/missing-is.stderr
new file mode 100644
index 0000000000000..8ed654fe9071a
--- /dev/null
+++ b/tests/ui/type/pattern_types/missing-is.stderr
@@ -0,0 +1,8 @@
+error: expected one of `!`, `(`, `+`, `::`, `<`, or `is`, found `0`
+  --> $DIR/missing-is.rs:6:30
+   |
+LL |     let x: pattern_type!(i32 0..1);
+   |                              ^ expected one of `!`, `(`, `+`, `::`, `<`, or `is`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/underscore-imports/issue-110164.stderr b/tests/ui/underscore-imports/issue-110164.stderr
index 240742996e12a..d8a4b6bbb7549 100644
--- a/tests/ui/underscore-imports/issue-110164.stderr
+++ b/tests/ui/underscore-imports/issue-110164.stderr
@@ -38,33 +38,25 @@ error[E0432]: unresolved import `_`
   --> $DIR/issue-110164.rs:8:5
    |
 LL | use _::*;
-   |     ^ you might be missing crate `_`
-   |
-   = help: consider adding `extern crate _` to use the `_` crate
+   |     ^ `_` is not a valid crate or module name
 
 error[E0432]: unresolved import `_`
   --> $DIR/issue-110164.rs:5:5
    |
 LL | use _::a;
-   |     ^ you might be missing crate `_`
-   |
-   = help: consider adding `extern crate _` to use the `_` crate
+   |     ^ `_` is not a valid crate or module name
 
 error[E0432]: unresolved import `_`
   --> $DIR/issue-110164.rs:13:9
    |
 LL |     use _::a;
-   |         ^ you might be missing crate `_`
-   |
-   = help: consider adding `extern crate _` to use the `_` crate
+   |         ^ `_` is not a valid crate or module name
 
 error[E0432]: unresolved import `_`
   --> $DIR/issue-110164.rs:16:9
    |
 LL |     use _::*;
-   |         ^ you might be missing crate `_`
-   |
-   = help: consider adding `extern crate _` to use the `_` crate
+   |         ^ `_` is not a valid crate or module name
 
 error: aborting due to 10 previous errors
 
diff --git a/tests/ui/unop/unop-move-semantics.stderr b/tests/ui/unop/unop-move-semantics.stderr
index bc9b3ea990384..0ae918d434a0f 100644
--- a/tests/ui/unop/unop-move-semantics.stderr
+++ b/tests/ui/unop/unop-move-semantics.stderr
@@ -40,7 +40,7 @@ help: if `T` implemented `Clone`, you could clone the value
 LL | fn move_borrowed>(x: T, mut y: T) {
    |                  ^ consider constraining this type parameter with `Clone`
 LL |     let m = &x;
-   |             -- you could clone this value
+   |              - you could clone this value
 
 error[E0505]: cannot move out of `y` because it is borrowed
   --> $DIR/unop-move-semantics.rs:17:6
@@ -63,7 +63,7 @@ LL | fn move_borrowed>(x: T, mut y: T) {
    |                  ^ consider constraining this type parameter with `Clone`
 LL |     let m = &x;
 LL |     let n = &mut y;
-   |             ------ you could clone this value
+   |                  - you could clone this value
 
 error[E0507]: cannot move out of `*m` which is behind a mutable reference
   --> $DIR/unop-move-semantics.rs:24:6
diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs
index 92c2e7b488478..29472df897a1f 100644
--- a/tests/ui/unpretty/expanded-exhaustive.rs
+++ b/tests/ui/unpretty/expanded-exhaustive.rs
@@ -25,7 +25,6 @@
 #![feature(trait_alias)]
 #![feature(try_blocks)]
 #![feature(unnamed_fields)]
-#![feature(unsafe_extern_blocks)]
 #![feature(yeet_expr)]
 #![allow(incomplete_features)]
 
diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout
index 137a8aa5510d0..cf2f6f8cbaa99 100644
--- a/tests/ui/unpretty/expanded-exhaustive.stdout
+++ b/tests/ui/unpretty/expanded-exhaustive.stdout
@@ -26,7 +26,6 @@
 #![feature(trait_alias)]
 #![feature(try_blocks)]
 #![feature(unnamed_fields)]
-#![feature(unsafe_extern_blocks)]
 #![feature(yeet_expr)]
 #![allow(incomplete_features)]
 #[prelude_import]
diff --git a/tests/ui/unresolved/unresolved-asterisk-imports.stderr b/tests/ui/unresolved/unresolved-asterisk-imports.stderr
index 299ec699775e9..ed01f3fdbea41 100644
--- a/tests/ui/unresolved/unresolved-asterisk-imports.stderr
+++ b/tests/ui/unresolved/unresolved-asterisk-imports.stderr
@@ -4,7 +4,10 @@ error[E0432]: unresolved import `not_existing_crate`
 LL | use not_existing_crate::*;
    |     ^^^^^^^^^^^^^^^^^^ you might be missing crate `not_existing_crate`
    |
-   = help: consider adding `extern crate not_existing_crate` to use the `not_existing_crate` crate
+help: consider importing the `not_existing_crate` crate
+   |
+LL + extern crate not_existing_crate;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/unresolved/unresolved-import.rs b/tests/ui/unresolved/unresolved-import.rs
index e8f3b323e3375..ee520d65e6f4a 100644
--- a/tests/ui/unresolved/unresolved-import.rs
+++ b/tests/ui/unresolved/unresolved-import.rs
@@ -1,7 +1,8 @@
 use foo::bar;
 //~^ ERROR unresolved import `foo` [E0432]
 //~| NOTE you might be missing crate `foo`
-//~| HELP consider adding `extern crate foo` to use the `foo` crate
+//~| HELP consider importing the `foo` crate
+//~| SUGGESTION extern crate foo;
 
 use bar::Baz as x;
 //~^ ERROR unresolved import `bar::Baz` [E0432]
diff --git a/tests/ui/unresolved/unresolved-import.stderr b/tests/ui/unresolved/unresolved-import.stderr
index 7b03717c827ce..a1ff2f19eb640 100644
--- a/tests/ui/unresolved/unresolved-import.stderr
+++ b/tests/ui/unresolved/unresolved-import.stderr
@@ -4,10 +4,13 @@ error[E0432]: unresolved import `foo`
 LL | use foo::bar;
    |     ^^^ you might be missing crate `foo`
    |
-   = help: consider adding `extern crate foo` to use the `foo` crate
+help: consider importing the `foo` crate
+   |
+LL + extern crate foo;
+   |
 
 error[E0432]: unresolved import `bar::Baz`
-  --> $DIR/unresolved-import.rs:6:5
+  --> $DIR/unresolved-import.rs:7:5
    |
 LL | use bar::Baz as x;
    |     ^^^^^---^^^^^
@@ -16,7 +19,7 @@ LL | use bar::Baz as x;
    |     no `Baz` in `bar`
 
 error[E0432]: unresolved import `food::baz`
-  --> $DIR/unresolved-import.rs:12:5
+  --> $DIR/unresolved-import.rs:13:5
    |
 LL | use food::baz;
    |     ^^^^^^---
@@ -25,7 +28,7 @@ LL | use food::baz;
    |     no `baz` in `food`
 
 error[E0432]: unresolved import `food::beens`
-  --> $DIR/unresolved-import.rs:18:12
+  --> $DIR/unresolved-import.rs:19:12
    |
 LL | use food::{beens as Foo};
    |            -----^^^^^^^
@@ -34,13 +37,13 @@ LL | use food::{beens as Foo};
    |            help: a similar name exists in the module: `beans`
 
 error[E0432]: unresolved import `MyEnum`
-  --> $DIR/unresolved-import.rs:43:9
+  --> $DIR/unresolved-import.rs:44:9
    |
 LL |     use MyEnum::*;
    |         ^^^^^^ help: a similar path exists: `self::MyEnum`
 
 error[E0432]: unresolved import `Enum`
-  --> $DIR/unresolved-import.rs:54:9
+  --> $DIR/unresolved-import.rs:55:9
    |
 LL |     use Enum::*;
    |         ^^^^ help: a similar path exists: `self::Enum`
diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr
index 0a810b7222e7c..21d8de6ae8815 100644
--- a/tests/ui/variance/variance-issue-20533.stderr
+++ b/tests/ui/variance/variance-issue-20533.stderr
@@ -17,7 +17,7 @@ LL | struct AffineU32(u32);
    | ^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |         let x = foo(&a);
-   |                     -- you could clone this value
+   |                      - you could clone this value
 
 error[E0505]: cannot move out of `a` because it is borrowed
   --> $DIR/variance-issue-20533.rs:41:14
@@ -38,7 +38,7 @@ LL | struct AffineU32(u32);
    | ^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |         let x = bar(&a);
-   |                     -- you could clone this value
+   |                      - you could clone this value
 
 error[E0505]: cannot move out of `a` because it is borrowed
   --> $DIR/variance-issue-20533.rs:47:14
@@ -59,7 +59,7 @@ LL | struct AffineU32(u32);
    | ^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |         let x = baz(&a);
-   |                     -- you could clone this value
+   |                      - you could clone this value
 
 error[E0505]: cannot move out of `a` because it is borrowed
   --> $DIR/variance-issue-20533.rs:53:14
@@ -80,7 +80,7 @@ LL | struct AffineU32(u32);
    | ^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type
 ...
 LL |         let x = bat(&a);
-   |                     -- you could clone this value
+   |                      - you could clone this value
 
 error[E0505]: cannot move out of `a` because it is borrowed
   --> $DIR/variance-issue-20533.rs:59:14
@@ -96,9 +96,8 @@ LL |         drop(x);
    |
 help: consider cloning the value if the performance cost is acceptable
    |
-LL -         let x = foo(&a);
-LL +         let x = foo(a.clone());
-   |
+LL |         let x = foo(&a.clone());
+   |                       ++++++++
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/wf/wf-in-where-clause-static.current.stderr b/tests/ui/wf/wf-in-where-clause-static.current.stderr
new file mode 100644
index 0000000000000..d0bb89884c68a
--- /dev/null
+++ b/tests/ui/wf/wf-in-where-clause-static.current.stderr
@@ -0,0 +1,12 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/wf-in-where-clause-static.rs:18:18
+   |
+LL |     let s = foo(&String::from("blah blah blah"));
+   |             -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement
+   |             |    |
+   |             |    creates a temporary value which is freed while still in use
+   |             argument requires that borrow lasts for `'static`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/wf/wf-in-where-clause-static.next.stderr b/tests/ui/wf/wf-in-where-clause-static.next.stderr
new file mode 100644
index 0000000000000..d0bb89884c68a
--- /dev/null
+++ b/tests/ui/wf/wf-in-where-clause-static.next.stderr
@@ -0,0 +1,12 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/wf-in-where-clause-static.rs:18:18
+   |
+LL |     let s = foo(&String::from("blah blah blah"));
+   |             -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement
+   |             |    |
+   |             |    creates a temporary value which is freed while still in use
+   |             argument requires that borrow lasts for `'static`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/wf/wf-in-where-clause-static.rs b/tests/ui/wf/wf-in-where-clause-static.rs
index a3d360e1fb56b..8ee654ef7cff4 100644
--- a/tests/ui/wf/wf-in-where-clause-static.rs
+++ b/tests/ui/wf/wf-in-where-clause-static.rs
@@ -1,9 +1,6 @@
-//@ check-pass
-//@ known-bug: #98117
-
-// Should fail. Functions are responsible for checking the well-formedness of
-// their own where clauses, so this should fail and require an explicit bound
-// `T: 'static`.
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 use std::fmt::Display;
 
@@ -19,5 +16,6 @@ where
 
 fn main() {
     let s = foo(&String::from("blah blah blah"));
+    //~^ ERROR temporary value dropped while borrowed
     println!("{}", s);
 }
diff --git a/triagebot.toml b/triagebot.toml
index 2795f93728470..33108f743cb94 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -778,6 +778,14 @@ If this was unintentional then you should revert the changes before this PR is m
 Otherwise, you can ignore this comment.
 """
 
+[mentions."library/Cargo.lock"]
+message = """
+These commits modify the `library/Cargo.lock` file. Unintentional changes to `library/Cargo.lock` can be introduced when switching branches and rebasing PRs.
+
+If this was unintentional then you should revert the changes before this PR is merged.
+Otherwise, you can ignore this comment.
+"""
+
 [mentions."src/tools/x"]
 message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version."